GNU Octave  4.4.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
graphics.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2007-2018 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
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License 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 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 #if defined (HAVE_CONFIG_H)
24 # include "config.h"
25 #endif
26 
27 #include <cctype>
28 #include <cmath>
29 #include <cstdint>
30 #include <cstdlib>
31 
32 #include <algorithm>
33 #include <iostream>
34 #include <limits>
35 #include <list>
36 #include <map>
37 #include <set>
38 #include <string>
39 #include <sstream>
40 
41 #include "cmd-edit.h"
42 #include "file-ops.h"
43 #include "file-stat.h"
44 #include "oct-locbuf.h"
45 #include "oct-time.h"
46 #include "singleton-cleanup.h"
47 
48 #include "builtin-defun-decls.h"
49 #include "defun.h"
50 #include "display.h"
51 #include "error.h"
52 #include "graphics.h"
53 #include "input.h"
54 #include "interpreter-private.h"
55 #include "interpreter.h"
56 #include "ov.h"
57 #include "ovl.h"
58 #include "oct-map.h"
59 #include "ov-fcn-handle.h"
60 #include "pager.h"
61 #include "parse.h"
62 #include "text-renderer.h"
63 #include "unwind-prot.h"
64 #include "utils.h"
65 #include "octave-default-image.h"
66 
67 // forward declarations
68 static octave_value xget (const graphics_handle& h, const caseless_str& name);
69 
70 OCTAVE_NORETURN static
71 void
73 {
74  error ("set: invalid value for %s property", pname.c_str ());
75 }
76 
77 // Check to see that PNAME matches just one of PNAMES uniquely.
78 // Return the full name of the match, or an empty caseless_str object
79 // if there is no match, or the match is ambiguous.
80 
81 static caseless_str
83  const std::set<std::string>& pnames,
84  const caseless_str& pname)
85 {
86  size_t len = pname.length ();
87  std::set<std::string> matches;
88 
89  // Find exact or partial matches to property name
90  for (const auto& propnm : pnames)
91  {
92  if (pname.compare (propnm, len))
93  {
94  if (len == propnm.length ())
95  return pname; // Exact match.
96 
97  matches.insert (propnm);
98  }
99  }
100 
101  size_t num_matches = matches.size ();
102 
103  if (num_matches == 0)
104  error ("%s: unknown %s property %s",
105  who.c_str (), what.c_str (), pname.c_str ());
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  std::string possible_match = *(matches.begin ());
123 
124  warning_with_id ("Octave:abbreviated-property-match",
125  "%s: allowing %s to match %s property %s",
126  who.c_str (), pname.c_str (), what.c_str (),
127  possible_match.c_str ());
128 
129  return possible_match;
130  }
131 
132  return caseless_str ();
133 }
134 
135 static Matrix
137 {
138  // The values below have been produced by viridis (64)(:)
139  // It would be nice to be able to feval the
140  // viridis function but since there is a static property object that includes
141  // a colormap_property object, we need to initialize this before main is
142  // even called, so calling an interpreted function is not possible.
143 
144  const double cmapv[] =
145  {
146  2.67004010000000e-01, 2.72651720952381e-01, 2.77106307619048e-01,
147  2.80356151428571e-01, 2.82390045238095e-01, 2.83204606666667e-01,
148  2.82809341428571e-01, 2.81230763333333e-01, 2.78516153333333e-01,
149  2.74735528571429e-01, 2.69981791904762e-01, 2.64368580952381e-01,
150  2.58026184285714e-01, 2.51098684761905e-01, 2.43732853333333e-01,
151  2.36073294285714e-01, 2.28263191428571e-01, 2.20424955714286e-01,
152  2.12666598571429e-01, 2.05079113809524e-01, 1.97721880952381e-01,
153  1.90631350000000e-01, 1.83819438571429e-01, 1.77272360952381e-01,
154  1.70957518571429e-01, 1.64832915714286e-01, 1.58845368095238e-01,
155  1.52951235714286e-01, 1.47131626666667e-01, 1.41402210952381e-01,
156  1.35832975714286e-01, 1.30582113809524e-01, 1.25898377619048e-01,
157  1.22163105714286e-01, 1.19872409523810e-01, 1.19626570000000e-01,
158  1.22045948571429e-01, 1.27667691904762e-01, 1.36834947142857e-01,
159  1.49643331428571e-01, 1.65967274285714e-01, 1.85538397142857e-01,
160  2.08030450000000e-01, 2.33127309523809e-01, 2.60531475238095e-01,
161  2.90000730000000e-01, 3.21329971428571e-01, 3.54355250000000e-01,
162  3.88930322857143e-01, 4.24933143333333e-01, 4.62246770476190e-01,
163  5.00753620000000e-01, 5.40336957142857e-01, 5.80861172380952e-01,
164  6.22170772857143e-01, 6.64087320476191e-01, 7.06403823333333e-01,
165  7.48885251428571e-01, 7.91273132857143e-01, 8.33302102380952e-01,
166  8.74717527142857e-01, 9.15296319047619e-01, 9.54839555238095e-01,
167  9.93247890000000e-01, 4.87433000000000e-03, 2.58456800000000e-02,
168  5.09139004761905e-02, 7.42014957142857e-02, 9.59536042857143e-02,
169  1.16893314761905e-01, 1.37350195714286e-01, 1.57479940000000e-01,
170  1.77347967619048e-01, 1.96969168571429e-01, 2.16330337619048e-01,
171  2.35404660952381e-01, 2.54161735714286e-01, 2.72573219047619e-01,
172  2.90619516666667e-01, 3.08291041428571e-01, 3.25586450952381e-01,
173  3.42517215238095e-01, 3.59102207142857e-01, 3.75366067142857e-01,
174  3.91340913333333e-01, 4.07061480000000e-01, 4.22563764285714e-01,
175  4.37885543809524e-01, 4.53062984285714e-01, 4.68129543809524e-01,
176  4.83117059523810e-01, 4.98052961428571e-01, 5.12959473333333e-01,
177  5.27854311428571e-01, 5.42750087142857e-01, 5.57652481904762e-01,
178  5.72563073333333e-01, 5.87476284285714e-01, 6.02382410952381e-01,
179  6.17265840000000e-01, 6.32106955714286e-01, 6.46881817142857e-01,
180  6.61562926190476e-01, 6.76119717142857e-01, 6.90518987142857e-01,
181  7.04725181904762e-01, 7.18700950000000e-01, 7.32406441904762e-01,
182  7.45802021904762e-01, 7.58846480000000e-01, 7.71497934761905e-01,
183  7.83714033809524e-01, 7.95453081428571e-01, 8.06673890000000e-01,
184  8.17337565714286e-01, 8.27409135714286e-01, 8.36858167619048e-01,
185  8.45663399523809e-01, 8.53815582857143e-01, 8.61321019047619e-01,
186  8.68206316666667e-01, 8.74522215714286e-01, 8.80346158571429e-01,
187  8.85780083333333e-01, 8.90945338571429e-01, 8.95973498571429e-01,
188  9.01005800000000e-01, 9.06156570000000e-01, 3.29415190000000e-01,
189  3.53367293333333e-01, 3.76236064761905e-01, 3.97901482857143e-01,
190  4.18250757142857e-01, 4.37178920000000e-01, 4.54595888571429e-01,
191  4.70433883333333e-01, 4.84653865714286e-01, 4.97250492857143e-01,
192  5.08254501428571e-01, 5.17731949047619e-01, 5.25780221428571e-01,
193  5.32522206190476e-01, 5.38097133333333e-01, 5.42651800000000e-01,
194  5.46335411904762e-01, 5.49287148571429e-01, 5.51635008571429e-01,
195  5.53493173333333e-01, 5.54953478571429e-01, 5.56089070000000e-01,
196  5.56952166666667e-01, 5.57576145714286e-01, 5.57974025714286e-01,
197  5.58142745238095e-01, 5.58058673809524e-01, 5.57684744285714e-01,
198  5.56973310000000e-01, 5.55864478571429e-01, 5.54288677142857e-01,
199  5.52175699047619e-01, 5.49445382857143e-01, 5.46023368571429e-01,
200  5.41830633809524e-01, 5.36795616666667e-01, 5.30847985714286e-01,
201  5.23924198571429e-01, 5.15966779523810e-01, 5.06924262857143e-01,
202  4.96751861428571e-01, 4.85412122857143e-01, 4.72873300000000e-01,
203  4.59105875238095e-01, 4.44095883333333e-01, 4.27825852857143e-01,
204  4.10292713809524e-01, 3.91487632857143e-01, 3.71420688571429e-01,
205  3.50098750000000e-01, 3.27544678571429e-01, 3.03798967142857e-01,
206  2.78916748571429e-01, 2.53000856190476e-01, 2.26223670000000e-01,
207  1.98879439523810e-01, 1.71494930000000e-01, 1.45037631428572e-01,
208  1.21291048571429e-01, 1.03326155238095e-01, 9.53507900000000e-02,
209  1.00469958095238e-01, 1.17876387142857e-01, 1.43936200000000e-01
210  };
211 
212  // It would be nice if Matrix had a ctor allowing to do the
213  // following without a copy
214  Matrix cmap (64, 3, 0.0);
215  std::copy (cmapv, cmapv + (64*3), cmap.fortran_vec ());
216  return cmap;
217 }
218 
219 static double
221 {
222  return octave::display_info::depth ();
223 }
224 
225 static Matrix
227 {
228  Matrix retval (1, 4);
229 
230  retval(0) = 1.0;
231  retval(1) = 1.0;
234 
235  return retval;
236 }
237 
238 static double
240 {
242 }
243 
244 static Matrix
246 {
247  Matrix retval (7, 3, 0.0);
248 
249  retval(0,1) = 0.447;
250  retval(0,2) = 0.741;
251 
252  retval(1,0) = 0.850;
253  retval(1,1) = 0.325;
254  retval(1,2) = 0.098;
255 
256  retval(2,0) = 0.929;
257  retval(2,1) = 0.694;
258  retval(2,2) = 0.125;
259 
260  retval(3,0) = 0.494;
261  retval(3,1) = 0.184;
262  retval(3,2) = 0.556;
263 
264  retval(4,0) = 0.466;
265  retval(4,1) = 0.674;
266  retval(4,2) = 0.188;
267 
268  retval(5,0) = 0.301;
269  retval(5,1) = 0.745;
270  retval(5,2) = 0.933;
271 
272  retval(6,0) = 0.635;
273  retval(6,1) = 0.078;
274  retval(6,2) = 0.184;
275 
276  return retval;
277 }
278 
279 static Matrix
280 default_lim (bool logscale = false)
281 {
282  Matrix m (1, 2);
283 
284  if (logscale)
285  {
286  m(0) = 0.1;
287  m(1) = 1.0;
288  }
289  else
290  {
291  m(0) = 0.0;
292  m(1) = 1.0;
293  }
294 
295  return m;
296 }
297 
298 static Matrix
300 {
301  Matrix retval (1, 2);
302 
303  retval(0) = 0;
304  retval(1) = 1;
305 
306  return retval;
307 }
308 
309 static Matrix
311 {
312  Matrix m (64, 64);
313 
314  int i = 0;
315  for (int col = 0; col < 64; col++)
316  for (int row = 0; row < 64; row++)
317  {
318  m(col,row) = static_cast<double> (default_im_data[i]);
319  i++;
320  }
321 
322  return m;
323 }
324 
325 static Matrix
327 {
328  Matrix m (3, 3);
329 
330  for (int col = 0; col < 3; col++)
331  for (int row = 0; row < 3; row++)
332  m(row,col) = col+1;
333 
334  return m;
335 }
336 
337 static Matrix
339 {
340  Matrix m (3, 3);
341 
342  for (int row = 0; row < 3; row++)
343  for (int col = 0; col < 3; col++)
344  m(row,col) = row+1;
345 
346  return m;
347 }
348 
349 static Matrix
351 {
352  Matrix m (3, 3, 0.0);
353 
354  for (int row = 0; row < 3; row++)
355  m(row,row) = 1.0;
356 
357  return m;
358 }
359 
360 static Matrix
362 {
363  return default_surface_zdata ();
364 }
365 
366 static Matrix
368 {
369  Matrix m (1, 3);
370 
371  m(0) = 1.0;
372  m(1) = 2.0;
373  m(2) = 3.0;
374 
375  return m;
376 }
377 
378 static Matrix
380 {
381  Matrix m (3, 2, 0.0);
382 
383  m(1) = 1.0;
384  m(3) = 1.0;
385  m(4) = 1.0;
386 
387  return m;
388 }
389 
390 static Matrix
392 {
393  Matrix m (3, 1, 0.0);
394 
395  m(1) = 1.0;
396 
397  return m;
398 }
399 
400 static Matrix
402 {
403  Matrix m (3, 1, 1.0);
404 
405  m(2) = 0.0;
406 
407  return m;
408 }
409 
410 static Matrix
412 {
413  Matrix m (1, 4);
414 
415  m(0) = 0.13;
416  m(1) = 0.11;
417  m(2) = 0.775;
418  m(3) = 0.815;
419 
420  return m;
421 }
422 
423 static Matrix
425 {
426  Matrix m (1, 4);
427 
428  m(0) = 0.0;
429  m(1) = 0.0;
430  m(2) = 1.0;
431  m(3) = 1.0;
432 
433  return m;
434 }
435 
436 static Matrix
438 {
439  Matrix m (1, 2);
440 
441  m(0) = 0.0;
442  m(1) = 90.0;
443 
444  return m;
445 }
446 
447 static Matrix
449 {
450  Matrix m (1, 6);
451 
452  m(0) = 0.0;
453  m(1) = 0.2;
454  m(2) = 0.4;
455  m(3) = 0.6;
456  m(4) = 0.8;
457  m(5) = 1.0;
458 
459  return m;
460 }
461 
462 static Matrix
464 {
465  Matrix m (1, 2);
466 
467  m(0) = 0.01;
468  m(1) = 0.025;
469 
470  return m;
471 }
472 
473 static Matrix
475 {
476  Matrix m (1, 4);
477 
478  m(0) = 300;
479  m(1) = 200;
480  m(2) = 560;
481  m(3) = 420;
482 
483  return m;
484 }
485 
486 static Matrix
488 {
489  Matrix m (1, 2);
490 
491  m(0) = 8.5;
492  m(1) = 11.0;
493 
494  return m;
495 }
496 
497 static Matrix
499 {
500  Matrix m (1, 4);
501 
502  m(0) = 0.25;
503  m(1) = 2.50;
504  m(2) = 8.00;
505  m(3) = 6.00;
506 
507  return m;
508 }
509 
510 static std::string
512 {
513  octave::gtk_manager& gtk_mgr
514  = octave::__get_gtk_manager__ ("default_graphics_toolkit");
515 
516  return gtk_mgr.default_toolkit ();
517 }
518 
519 static Matrix
521 {
522  Matrix retval (1, 4);
523 
524  retval(0) = 0;
525  retval(1) = 0;
526  retval(2) = 80;
527  retval(3) = 30;
528 
529  return retval;
530 }
531 
532 static Matrix
534 {
535  Matrix retval (1, 2);
536 
537  retval(0) = 0.01;
538  retval(1) = 0.1;
539 
540  return retval;
541 }
542 
543 static Matrix
545 {
546  Matrix retval (1, 4);
547 
548  retval(0) = 0;
549  retval(1) = 0;
550  retval(2) = 1;
551  retval(3) = 1;
552 
553  return retval;
554 }
555 
556 static Matrix
558 {
559  Matrix m (1, 3);
560 
561  m(0) = 1.0;
562  m(1) = 0.0;
563  m(2) = 1.0;
564 
565  return m;
566 }
567 
568 static double
569 convert_font_size (double font_size, const caseless_str& from_units,
570  const caseless_str& to_units, double parent_height = 0)
571 {
572  // Simple case where from_units == to_units
573 
574  if (from_units.compare (to_units))
575  return font_size;
576 
577  // Converts the given fontsize using the following transformation:
578  // <old_font_size> => points => <new_font_size>
579 
580  double points_size = 0;
581  double res = 0;
582 
583  if (from_units.compare ("points"))
584  points_size = font_size;
585  else
586  {
587  res = xget (0, "screenpixelsperinch").double_value ();
588 
589  if (from_units.compare ("pixels"))
590  points_size = font_size * 72.0 / res;
591  else if (from_units.compare ("inches"))
592  points_size = font_size * 72.0;
593  else if (from_units.compare ("centimeters"))
594  points_size = font_size * 72.0 / 2.54;
595  else if (from_units.compare ("normalized"))
596  points_size = font_size * parent_height * 72.0 / res;
597  }
598 
599  double new_font_size = 0;
600 
601  if (to_units.compare ("points"))
602  new_font_size = points_size;
603  else
604  {
605  if (res <= 0)
606  res = xget (0, "screenpixelsperinch").double_value ();
607 
608  if (to_units.compare ("pixels"))
609  new_font_size = points_size * res / 72.0;
610  else if (to_units.compare ("inches"))
611  new_font_size = points_size / 72.0;
612  else if (to_units.compare ("centimeters"))
613  new_font_size = points_size * 2.54 / 72.0;
614  else if (to_units.compare ("normalized"))
615  {
616  // Avoid setting font size to (0/0) = NaN
617 
618  if (parent_height > 0)
619  new_font_size = points_size * res / (parent_height * 72.0);
620  }
621  }
622 
623  return new_font_size;
624 }
625 
626 static Matrix
627 convert_position (const Matrix& pos, const caseless_str& from_units,
628  const caseless_str& to_units, const Matrix& parent_dim)
629 {
630  Matrix retval (1, pos.numel ());
631  double res = 0;
632  bool is_rectangle = (pos.numel () == 4);
633  bool is_2d = (pos.numel () == 2);
634 
635  if (from_units.compare ("pixels"))
636  retval = pos;
637  else if (from_units.compare ("normalized"))
638  {
639  retval(0) = pos(0) * parent_dim(0) + 1;
640  retval(1) = pos(1) * parent_dim(1) + 1;
641  if (is_rectangle)
642  {
643  retval(2) = pos(2) * parent_dim(0);
644  retval(3) = pos(3) * parent_dim(1);
645  }
646  else if (! is_2d)
647  retval(2) = 0;
648  }
649  else if (from_units.compare ("characters"))
650  {
651  if (res <= 0)
652  res = xget (0, "screenpixelsperinch").double_value ();
653 
654  double f = 0.0;
655 
656  // FIXME: this assumes the system font is Helvetica 10pt
657  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
658  f = 12.0 * res / 74.951;
659 
660  if (f > 0)
661  {
662  retval(0) = 0.5 * pos(0) * f;
663  retval(1) = pos(1) * f;
664  if (is_rectangle)
665  {
666  retval(2) = 0.5 * pos(2) * f;
667  retval(3) = pos(3) * f;
668  }
669  else if (! is_2d)
670  retval(2) = 0;
671  }
672  }
673  else
674  {
675  if (res <= 0)
676  res = xget (0, "screenpixelsperinch").double_value ();
677 
678  double f = 0.0;
679 
680  if (from_units.compare ("points"))
681  f = res / 72.0;
682  else if (from_units.compare ("inches"))
683  f = res;
684  else if (from_units.compare ("centimeters"))
685  f = res / 2.54;
686 
687  if (f > 0)
688  {
689  retval(0) = pos(0) * f + 1;
690  retval(1) = pos(1) * f + 1;
691  if (is_rectangle)
692  {
693  retval(2) = pos(2) * f;
694  retval(3) = pos(3) * f;
695  }
696  else if (! is_2d)
697  retval(2) = 0;
698  }
699  }
700 
701  if (! to_units.compare ("pixels"))
702  {
703  if (to_units.compare ("normalized"))
704  {
705  retval(0) = (retval(0) - 1) / parent_dim(0);
706  retval(1) = (retval(1) - 1) / parent_dim(1);
707  if (is_rectangle)
708  {
709  retval(2) /= parent_dim(0);
710  retval(3) /= parent_dim(1);
711  }
712  else if (! is_2d)
713  retval(2) = 0;
714  }
715  else if (to_units.compare ("characters"))
716  {
717  if (res <= 0)
718  res = xget (0, "screenpixelsperinch").double_value ();
719 
720  double f = 0.0;
721 
722  f = 12.0 * res / 74.951;
723 
724  if (f > 0)
725  {
726  retval(0) = 2 * retval(0) / f;
727  retval(1) = retval(1) / f;
728  if (is_rectangle)
729  {
730  retval(2) = 2 * retval(2) / f;
731  retval(3) = retval(3) / f;
732  }
733  else if (! is_2d)
734  retval(2) = 0;
735  }
736  }
737  else
738  {
739  if (res <= 0)
740  res = xget (0, "screenpixelsperinch").double_value ();
741 
742  double f = 0.0;
743 
744  if (to_units.compare ("points"))
745  f = res / 72.0;
746  else if (to_units.compare ("inches"))
747  f = res;
748  else if (to_units.compare ("centimeters"))
749  f = res / 2.54;
750 
751  if (f > 0)
752  {
753  retval(0) = (retval(0) - 1) / f;
754  retval(1) = (retval(1) - 1) / f;
755  if (is_rectangle)
756  {
757  retval(2) /= f;
758  retval(3) /= f;
759  }
760  else if (! is_2d)
761  retval(2) = 0;
762  }
763  }
764  }
765  else if (! is_rectangle && ! is_2d)
766  retval(2) = 0;
767 
768  return retval;
769 }
770 
771 static Matrix
773  const caseless_str& from_units,
774  const caseless_str& to_units)
775 {
776  graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
777  graphics_object ax = go.get_ancestor ("axes");
778 
779  Matrix retval;
780 
781  if (ax.valid_object ())
782  {
783  const axes::properties& ax_props =
784  dynamic_cast<const axes::properties&> (ax.get_properties ());
785  graphics_xform ax_xform = ax_props.get_transform ();
786  bool is_rectangle = (pos.numel () == 4);
787  Matrix ax_bbox = ax_props.get_boundingbox (true),
788  ax_size = ax_bbox.extract_n (0, 2, 1, 2);
789 
790  if (from_units.compare ("data"))
791  {
792  if (is_rectangle)
793  {
794  ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
795  v2 = ax_xform.transform (pos(0) + pos(2),
796  pos(1) + pos(3), 0);
797 
798  retval.resize (1, 4);
799 
800  retval(0) = v1(0) - ax_bbox(0) + 1;
801  retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
802  retval(2) = v2(0) - v1(0);
803  retval(3) = v1(1) - v2(1);
804  }
805  else
806  {
807  ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
808 
809  retval.resize (1, 3);
810 
811  retval(0) = v(0) - ax_bbox(0) + 1;
812  retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
813  retval(2) = 0;
814  }
815  }
816  else
817  retval = convert_position (pos, from_units, "pixels", ax_size);
818 
819  if (! to_units.compare ("pixels"))
820  {
821  if (to_units.compare ("data"))
822  {
823  if (is_rectangle)
824  {
825  ColumnVector v1, v2;
826  v1 = ax_xform.untransform (
827  retval(0) + ax_bbox(0) - 1,
828  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
829  v2 = ax_xform.untransform (
830  retval(0) + retval(2) + ax_bbox(0) - 1,
831  ax_bbox(1) + ax_bbox(3) - (retval(1) + retval(3)) + 1);
832 
833  retval.resize (1, 4);
834 
835  retval(0) = v1(0);
836  retval(1) = v1(1);
837  retval(2) = v2(0) - v1(0);
838  retval(3) = v2(1) - v1(1);
839  }
840  else
841  {
842  ColumnVector v;
843  v = ax_xform.untransform (
844  retval(0) + ax_bbox(0) - 1,
845  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
846 
847  retval.resize (1, 3);
848 
849  retval(0) = v(0);
850  retval(1) = v(1);
851  retval(2) = v(2);
852  }
853  }
854  else
855  retval = convert_position (retval, "pixels", to_units, ax_size);
856  }
857  }
858 
859  return retval;
860 }
861 
862 // This function always returns the screensize in pixels
863 static Matrix
865 {
867  Matrix sz = obj.get ("screensize").matrix_value ();
868  return convert_position (sz, obj.get ("units").string_value (), "pixels",
869  sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
870 }
871 
872 static void
873 convert_cdata_2 (bool is_scaled, bool is_real, double clim_0, double clim_1,
874  const double *cmapv, double x, octave_idx_type lda,
875  octave_idx_type nc, octave_idx_type i, double *av)
876 {
877  if (is_scaled)
878  x = octave::math::fix (nc * (x - clim_0) / (clim_1 - clim_0));
879  else if (is_real)
880  x = octave::math::fix (x - 1);
881 
882  if (octave::math::isnan (x))
883  {
884  av[i] = x;
885  av[i+lda] = x;
886  av[i+2*lda] = x;
887  }
888  else
889  {
890  if (x < 0)
891  x = 0;
892  else if (x >= nc)
893  x = (nc - 1);
894 
895  octave_idx_type idx = static_cast<octave_idx_type> (x);
896 
897  av[i] = cmapv[idx];
898  av[i+lda] = cmapv[idx+nc];
899  av[i+2*lda] = cmapv[idx+2*nc];
900  }
901 }
902 
903 template <typename T>
904 void
905 convert_cdata_1 (bool is_scaled, bool is_real, double clim_0, double clim_1,
906  const double *cmapv, const T *cv, octave_idx_type lda,
907  octave_idx_type nc, double *av)
908 {
909  for (octave_idx_type i = 0; i < lda; i++)
910  convert_cdata_2 (is_scaled, is_real,
911  clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
912 }
913 
914 static octave_value
915 convert_cdata (const base_properties& props, const octave_value& cdata,
916  bool is_scaled, int cdim)
917 {
918  dim_vector dv (cdata.dims ());
919 
920  // TrueColor data doesn't require conversion
921  if (dv.ndims () == cdim && dv(cdim-1) == 3)
922  return cdata;
923 
924  Matrix cmap (1, 3, 0.0);
925  Matrix clim (1, 2, 0.0);
926 
927  graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
928  graphics_object ax = go.get_ancestor ("axes");
929 
930  if (ax.valid_object ())
931  {
932  Matrix _cmap = ax.get (caseless_str ("colormap")).matrix_value ();
933 
934  cmap = _cmap;
935 
936  if (is_scaled)
937  {
938  Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
939 
940  clim = _clim;
941  }
942  }
943 
944  dv.resize (cdim);
945  dv(cdim-1) = 3;
946 
947  NDArray a (dv);
948 
949  octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
950  octave_idx_type nc = cmap.rows ();
951 
952  double *av = a.fortran_vec ();
953  const double *cmapv = cmap.data ();
954 
955  double clim_0 = clim(0);
956  double clim_1 = clim(1);
957 
958  // FIXME: There is a lot of processing time spent just on data conversion
959  // both here in graphics.cc and again in gl-render.cc. There must
960  // be room for improvement! Here a macro expands to a templated
961  // function which in turn calls another function (covert_cdata_2).
962  // And in gl-render.cc (opengl_renderer::draw_image), only GLfloat
963  // is supported anyways so there is another double for loop across
964  // height and width to convert all of the input data to GLfloat.
965 
966 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL) \
967  do \
968  { \
969  ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
970  \
971  convert_cdata_1 (is_scaled, IS_REAL, clim_0, clim_1, cmapv, \
972  tmp.data (), lda, nc, av); \
973  } \
974  while (0)
975 
976  if (cdata.is_uint8_type ())
977  CONVERT_CDATA_1 (uint8NDArray, uint8_, false);
978  else if (cdata.is_uint16_type ())
979  CONVERT_CDATA_1 (uint16NDArray, uint16_, false);
980  else if (cdata.is_double_type ())
981  CONVERT_CDATA_1 (NDArray, , true);
982  else if (cdata.is_single_type ())
983  CONVERT_CDATA_1 (FloatNDArray, float_, true);
984  else if (cdata.islogical ())
985  CONVERT_CDATA_1 (boolNDArray, bool_, false);
986  else
987  {
988  // Don't throw an error; leads to an incomplete FLTK object (bug #46933).
989  warning ("unsupported type for cdata (= %s). "
990  "Valid types are uint8, uint16, double, single, and bool.",
991  cdata.type_name ().c_str ());
992  a = NDArray (dv, 0); // return 0 instead
993  }
994 
995 #undef CONVERT_CDATA_1
996 
997  return octave_value (a);
998 }
999 
1000 template <typename T>
1001 static void
1002 get_array_limits (const Array<T>& m, double& emin, double& emax,
1003  double& eminp, double& emaxp)
1004 {
1005  const T *data = m.data ();
1006  octave_idx_type n = m.numel ();
1007 
1008  for (octave_idx_type i = 0; i < n; i++)
1009  {
1010  double e = double (data[i]);
1011 
1012  // Don't need to test for NaN here as NaN>x and NaN<x is always false
1013  if (! octave::math::isinf (e))
1014  {
1015  if (e < emin)
1016  emin = e;
1017 
1018  if (e > emax)
1019  emax = e;
1020 
1021  if (e > 0 && e < eminp)
1022  eminp = e;
1023 
1024  if (e < 0 && e > emaxp)
1025  emaxp = e;
1026  }
1027  }
1028 }
1029 
1030 static bool
1032  caseless_str& rest)
1033 {
1034  int len = name.length ();
1035  int offset = 0;
1036  bool result = false;
1037 
1038  if (len >= 4)
1039  {
1040  caseless_str pfx = name.substr (0, 4);
1041 
1042  if (pfx.compare ("axes") || pfx.compare ("line")
1043  || pfx.compare ("text"))
1044  offset = 4;
1045  else if (len >= 5)
1046  {
1047  pfx = name.substr (0, 5);
1048 
1049  if (pfx.compare ("image") || pfx.compare ("patch"))
1050  offset = 5;
1051  else if (len >= 6)
1052  {
1053  pfx = name.substr (0, 6);
1054 
1055  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1056  offset = 6;
1057  else if (len >= 7)
1058  {
1059  pfx = name.substr (0, 7);
1060 
1061  if (pfx.compare ("surface") || pfx.compare ("hggroup")
1062  || pfx.compare ("uipanel"))
1063  offset = 7;
1064  else if (len >= 9)
1065  {
1066  pfx = name.substr (0, 9);
1067 
1068  if (pfx.compare ("uicontrol")
1069  || pfx.compare ("uitoolbar"))
1070  offset = 9;
1071  else if (len >= 10)
1072  {
1073  pfx = name.substr (0, 10);
1074 
1075  if (pfx.compare ("uipushtool"))
1076  offset = 10;
1077  else if (len >= 12)
1078  {
1079  pfx = name.substr (0, 12);
1080 
1081  if (pfx.compare ("uitoggletool"))
1082  offset = 12;
1083  else if (len >= 13)
1084  {
1085  pfx = name.substr (0, 13);
1086 
1087  if (pfx.compare ("uicontextmenu") ||
1088  pfx.compare ("uibuttongroup"))
1089  offset = 13;
1090  }
1091  }
1092  }
1093  }
1094  }
1095  }
1096  }
1097 
1098  if (offset > 0)
1099  {
1100  go_name = pfx;
1101  rest = name.substr (offset);
1102  result = true;
1103  }
1104  }
1105 
1106  return result;
1107 }
1108 
1109 static base_graphics_object*
1111  const graphics_handle& h = graphics_handle (),
1112  const graphics_handle& p = graphics_handle ())
1113 {
1114  base_graphics_object *go = nullptr;
1115 
1116  if (type.compare ("figure"))
1117  go = new figure (h, p);
1118  else if (type.compare ("axes"))
1119  go = new axes (h, p);
1120  else if (type.compare ("line"))
1121  go = new line (h, p);
1122  else if (type.compare ("text"))
1123  go = new text (h, p);
1124  else if (type.compare ("image"))
1125  go = new image (h, p);
1126  else if (type.compare ("light"))
1127  go = new light (h, p);
1128  else if (type.compare ("patch"))
1129  go = new patch (h, p);
1130  else if (type.compare ("surface"))
1131  go = new surface (h, p);
1132  else if (type.compare ("hggroup"))
1133  go = new hggroup (h, p);
1134  else if (type.compare ("uimenu"))
1135  go = new uimenu (h, p);
1136  else if (type.compare ("uicontrol"))
1137  go = new uicontrol (h, p);
1138  else if (type.compare ("uipanel"))
1139  go = new uipanel (h, p);
1140  else if (type.compare ("uibuttongroup"))
1141  go = new uibuttongroup (h, p);
1142  else if (type.compare ("uicontextmenu"))
1143  go = new uicontextmenu (h, p);
1144  else if (type.compare ("uitoolbar"))
1145  go = new uitoolbar (h, p);
1146  else if (type.compare ("uipushtool"))
1147  go = new uipushtool (h, p);
1148  else if (type.compare ("uitoggletool"))
1149  go = new uitoggletool (h, p);
1150  return go;
1151 }
1152 
1153 // ---------------------------------------------------------------------
1154 
1155 bool
1156 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
1157 {
1158  if (do_set (v))
1159  {
1160  // Notify graphics toolkit.
1161  if (id >= 0 && do_notify_toolkit)
1162  {
1164  if (go)
1165  go.update (id);
1166  }
1167 
1168  // run listeners
1169  if (do_run)
1171 
1172  return true;
1173  }
1174 
1175  return false;
1176 }
1177 
1178 void
1180 {
1181  const octave_value_list& l = listeners[mode];
1182 
1183  for (int i = 0; i < l.length (); i++)
1185 }
1186 
1188  : default_val (), possible_vals ()
1189 {
1190  size_t beg = 0;
1191  size_t len = opt_string.length ();
1192  bool done = len == 0;
1193 
1194  while (! done)
1195  {
1196  size_t end = opt_string.find ('|', beg);
1197 
1198  if (end == std::string::npos)
1199  {
1200  end = len;
1201  done = true;
1202  }
1203 
1204  std::string t = opt_string.substr (beg, end-beg);
1205 
1206  // Might want more error checking here...
1207  if (t[0] == '{')
1208  {
1209  t = t.substr (1, t.length () - 2);
1210  default_val = t;
1211  }
1212  else if (beg == 0) // ensure default value
1213  default_val = t;
1214 
1215  possible_vals.insert (t);
1216 
1217  beg = end + 1;
1218  }
1219 }
1220 
1223 {
1225 
1226  for (const auto& val : possible_vals)
1227  {
1228  if (retval.empty ())
1229  {
1230  if (val == default_value ())
1231  retval = '{' + val + '}';
1232  else
1233  retval = val;
1234  }
1235  else
1236  {
1237  if (val == default_value ())
1238  retval += " | {" + val + '}';
1239  else
1240  retval += " | " + val;
1241  }
1242  }
1243 
1244  if (! retval.empty ())
1245  retval = "[ " + retval + " ]";
1246 
1247  return retval;
1248 }
1249 
1250 Cell
1252 {
1253  octave_idx_type i = 0;
1254  Cell retval (nelem (), 1);
1255 
1256  for (const auto& val : possible_vals)
1257  retval(i++) = std::string (val);
1258 
1259  return retval;
1260 }
1261 
1262 bool
1264 {
1265  bool retval = true;
1266 
1267  double tmp_rgb[3] = {0, 0, 0};
1268 
1269  std::string str = str_arg;
1270  unsigned int len = str.length ();
1271 
1272  std::transform (str.begin (), str.end (), str.begin (), tolower);
1273 
1274  if (str.compare (0, len, "blue", 0, len) == 0)
1275  tmp_rgb[2] = 1;
1276  else if (str.compare (0, len, "black", 0, len) == 0
1277  || str.compare (0, len, "k", 0, len) == 0)
1278  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
1279  else if (str.compare (0, len, "red", 0, len) == 0)
1280  tmp_rgb[0] = 1;
1281  else if (str.compare (0, len, "green", 0, len) == 0)
1282  tmp_rgb[1] = 1;
1283  else if (str.compare (0, len, "yellow", 0, len) == 0)
1284  tmp_rgb[0] = tmp_rgb[1] = 1;
1285  else if (str.compare (0, len, "magenta", 0, len) == 0)
1286  tmp_rgb[0] = tmp_rgb[2] = 1;
1287  else if (str.compare (0, len, "cyan", 0, len) == 0)
1288  tmp_rgb[1] = tmp_rgb[2] = 1;
1289  else if (str.compare (0, len, "white", 0, len) == 0
1290  || str.compare (0, len, "w", 0, len) == 0)
1291  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
1292  else
1293  retval = false;
1294 
1295  if (retval)
1296  {
1297  for (int i = 0; i < 3; i++)
1298  xrgb(i) = tmp_rgb[i];
1299  }
1300 
1301  return retval;
1302 }
1303 
1304 bool
1306 {
1307  if (val.is_string ())
1308  {
1309  std::string s = val.string_value ();
1310 
1311  if (s.empty ())
1312  error (R"(invalid value for color property "%s")",
1313  get_name ().c_str ());
1314 
1315  std::string match;
1316 
1317  if (radio_val.contains (s, match))
1318  {
1319  if (current_type != radio_t || match != current_val)
1320  {
1321  if (s.length () != match.length ())
1322  warning_with_id ("Octave:abbreviated-property-match",
1323  "%s: allowing %s to match %s value %s",
1324  "set", s.c_str (), get_name ().c_str (),
1325  match.c_str ());
1326  current_val = match;
1328  return true;
1329  }
1330  }
1331  else
1332  {
1333  try
1334  {
1335  color_values col (s);
1336 
1337  if (current_type != color_t || col != color_val)
1338  {
1339  color_val = col;
1341  return true;
1342  }
1343  }
1344  catch (octave::execution_exception& e)
1345  {
1346  error (e, R"(invalid value for color property "%s" (value = %s))",
1347  get_name ().c_str (), s.c_str ());
1348  }
1349  }
1350  }
1351  else if (val.isnumeric ())
1352  {
1353  Matrix m = val.matrix_value ();
1354 
1355  if (m.numel () != 3)
1356  error (R"(invalid value for color property "%s")",
1357  get_name ().c_str ());
1358 
1359  color_values col (m(0), m(1), m(2));
1360 
1361  if (current_type != color_t || col != color_val)
1362  {
1363  color_val = col;
1365  return true;
1366  }
1367  }
1368  else
1369  error (R"(invalid value for color property "%s")",
1370  get_name ().c_str ());
1371 
1372  return false;
1373 }
1374 
1375 bool
1377 {
1378  if (val.is_string ())
1379  {
1380  std::string s = val.string_value ();
1381  std::string match;
1382 
1383  if (s.empty () || ! radio_val.contains (s, match))
1384  error (R"(invalid value for double_radio property "%s")",
1385  get_name ().c_str ());
1386 
1387  if (current_type != radio_t || match != current_val)
1388  {
1389  if (s.length () != match.length ())
1390  warning_with_id ("Octave:abbreviated-property-match",
1391  "%s: allowing %s to match %s value %s",
1392  "set", s.c_str (), get_name ().c_str (),
1393  match.c_str ());
1394  current_val = match;
1396  return true;
1397  }
1398  }
1399  else if (val.is_scalar_type () && val.isreal ())
1400  {
1401  double new_dval = val.double_value ();
1402 
1403  if (current_type != double_t || new_dval != dval)
1404  {
1405  dval = new_dval;
1407  return true;
1408  }
1409  }
1410  else
1411  error (R"(invalid value for double_radio property "%s")",
1412  get_name ().c_str ());
1413 
1414  return false;
1415 }
1416 
1417 bool
1419 {
1420  bool xok = false;
1421 
1422  // check value type
1423  if (type_constraints.size () > 0)
1424  {
1425  if (type_constraints.find (v.class_name ()) != type_constraints.end ())
1426  xok = true;
1427 
1428  // check if complex is allowed (it's also of class "double", so
1429  // checking that alone is not enough to ensure real type)
1430  if (type_constraints.find ("real") != type_constraints.end ()
1431  && v.iscomplex ())
1432  xok = false;
1433  }
1434  else
1435  xok = v.isnumeric () || v.is_bool_scalar ();
1436 
1437  if (xok && size_constraints.size () > 0)
1438  {
1439  dim_vector vdims = v.dims ();
1440  int vlen = vdims.ndims ();
1441 
1442  xok = false;
1443 
1444  // check dimensional size constraints until a match is found
1445  for (auto it = size_constraints.cbegin ();
1446  ! xok && it != size_constraints.cend ();
1447  ++it)
1448  {
1449  dim_vector itdims = (*it);
1450 
1451  if (itdims.ndims () == vlen)
1452  {
1453  xok = true;
1454 
1455  for (int i = 0; xok && i < vlen; i++)
1456  {
1457  if (itdims(i) > 0)
1458  {
1459  if (itdims(i) != vdims(i))
1460  xok = false;
1461  }
1462  else if (v.isempty ())
1463  break;
1464  }
1465  }
1466  }
1467  }
1468 
1469  if (xok)
1470  {
1471  NDArray v_mat = v.array_value ();
1472  // Check min and max
1473  if (! octave::math::isnan (minval.first))
1474  {
1475  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1476  if (minval.second && minval.first > v_mat(i))
1477  error (R"(set: "%s" must be greater than or equal to %g)",
1478  get_name ().c_str (), minval.first);
1479  else if (! minval.second && minval.first >= v_mat(i))
1480  error (R"(set: "%s" must be greater than %g)",
1481  get_name ().c_str (), minval.first);
1482  }
1483 
1484  if (! octave::math::isnan (maxval.first))
1485  {
1486  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1487  if (maxval.second && maxval.first < v_mat(i))
1488  error (R"(set: "%s" must be less than or equal to %g)",
1489  get_name ().c_str (), maxval.first);
1490  else if (! maxval.second && maxval.first <= v_mat(i))
1491  error (R"(set: "%s" must be less than %g)",
1492  get_name ().c_str (), maxval.first);
1493  }
1494 
1495  if (finite_constraint == NO_CHECK) { /* do nothing */ }
1496  else if (finite_constraint == FINITE)
1497  {
1498  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1499  if (! octave::math::isfinite (v_mat(i)))
1500  error (R"(set: "%s" must be finite)", get_name ().c_str ());
1501  }
1502  else if (finite_constraint == NOT_NAN)
1503  {
1504  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1505  if (octave::math::isnan (v_mat(i)))
1506  error (R"(set: "%s" must not be nan)", get_name ().c_str ());
1507  }
1508  else if (finite_constraint == NOT_INF)
1509  {
1510  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1511  if (octave::math::isinf (v_mat(i)))
1512  error (R"(set: "%s" must not be infinite)", get_name ().c_str ());
1513  }
1514 
1515  }
1516 
1517  return xok;
1518 }
1519 
1520 bool
1522 {
1523  if (data.type_name () == v.type_name ())
1524  {
1525  if (data.dims () == v.dims ())
1526  {
1527 
1528 #define CHECK_ARRAY_EQUAL(T, F, A) \
1529  { \
1530  if (data.numel () == 1) \
1531  return data.F ## scalar_value () == \
1532  v.F ## scalar_value (); \
1533  else \
1534  { \
1535  /* Keep copy of array_value to allow */ \
1536  /* sparse/bool arrays that are converted, to */ \
1537  /* not be deallocated early */ \
1538  const A m1 = data.F ## array_value (); \
1539  const T *d1 = m1.data (); \
1540  const A m2 = v.F ## array_value (); \
1541  const T *d2 = m2.data (); \
1542  \
1543  bool flag = true; \
1544  \
1545  for (int i = 0; flag && i < data.numel (); i++) \
1546  if (d1[i] != d2[i]) \
1547  flag = false; \
1548  \
1549  return flag; \
1550  } \
1551  }
1552 
1553  if (data.is_double_type () || data.islogical ())
1554  CHECK_ARRAY_EQUAL (double, , NDArray)
1555  else if (data.is_single_type ())
1556  CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
1557  else if (data.is_int8_type ())
1559  else if (data.is_int16_type ())
1561  else if (data.is_int32_type ())
1563  else if (data.is_int64_type ())
1565  else if (data.is_uint8_type ())
1567  else if (data.is_uint16_type ())
1569  else if (data.is_uint32_type ())
1571  else if (data.is_uint64_type ())
1573  }
1574  }
1575 
1576  return false;
1577 }
1578 
1579 void
1581 {
1584 
1585  if (! data.isempty ())
1586  {
1587  if (data.isinteger ())
1588  {
1589  if (data.is_int8_type ())
1591  xmin, xmax, xminp, xmaxp);
1592  else if (data.is_uint8_type ())
1594  xmin, xmax, xminp, xmaxp);
1595  else if (data.is_int16_type ())
1597  xmin, xmax, xminp, xmaxp);
1598  else if (data.is_uint16_type ())
1600  xmin, xmax, xminp, xmaxp);
1601  else if (data.is_int32_type ())
1603  xmin, xmax, xminp, xmaxp);
1604  else if (data.is_uint32_type ())
1606  xmin, xmax, xminp, xmaxp);
1607  else if (data.is_int64_type ())
1609  xmin, xmax, xminp, xmaxp);
1610  else if (data.is_uint64_type ())
1612  xmin, xmax, xminp, xmaxp);
1613  }
1614  else
1616  }
1617 }
1618 
1619 bool
1621 {
1622  // Users may want to use empty matrix to reset a handle property
1623  if (v.isempty ())
1624  {
1625  if (! get ().isempty ())
1626  {
1628  return true;
1629  }
1630  else
1631  return false;
1632  }
1633 
1634  double dv = v.xdouble_value (R"(set: invalid graphics handle for property "%s")",
1635  get_name ().c_str ());
1636 
1638 
1639  // Check the object type if necessary
1640  bool type_ok = true;
1641  if (gh.ok () && ! type_constraints.empty ())
1642  {
1643  type_ok = false;
1645 
1646  for (const auto& type : type_constraints)
1647  if (obj.isa (type))
1648  {
1649  type_ok = true;
1650  break;
1651  }
1652  }
1653 
1654  if (! octave::math::isnan (gh.value ()) && ! (gh.ok () && type_ok))
1655  {
1656  if (type_ok)
1657  error (R"(set: invalid graphics handle (= %g) for property "%s")",
1658  dv, get_name ().c_str ());
1659  else
1660  error (R"(set: invalid graphics object type for property "%s")",
1661  get_name ().c_str ());
1662  }
1663 
1664  if (current_val != gh)
1665  {
1666  current_val = gh;
1667  return true;
1668  }
1669 
1670  return false;
1671 }
1672 
1673 /*
1674 ## Test validation of uicontextmenu property
1675 %!test
1676 %! hf = figure ("visible", "off");
1677 %! unwind_protect
1678 %! hax = axes ("parent", hf);
1679 %! hpa = patch ("parent", hax);
1680 %! try
1681 %! set (hax, "uicontextmenu", hpa);
1682 %! catch
1683 %! err = lasterr ();
1684 %! end_try_catch
1685 %! assert (err, 'set: invalid graphics object type for property "uicontextmenu"');
1686 %! unwind_protect_cleanup
1687 %! delete (hf);
1688 %! end_unwind_protect
1689 */
1690 
1691 Matrix
1692 children_property::do_get_children (bool return_hidden) const
1693 {
1694  Matrix retval (children_list.size (), 1);
1695  octave_idx_type k = 0;
1696 
1698 
1699  root_figure::properties& props =
1700  dynamic_cast<root_figure::properties&> (go.get_properties ());
1701 
1702  if (! props.is_showhiddenhandles ())
1703  {
1704  for (const auto& hchild : children_list)
1705  {
1706  graphics_handle kid = hchild;
1707 
1709  {
1710  if (! return_hidden)
1711  retval(k++) = hchild;
1712  }
1713  else if (return_hidden)
1714  retval(k++) = hchild;
1715  }
1716 
1717  retval.resize (k, 1);
1718  }
1719  else
1720  {
1721  for (const auto& hchild : children_list)
1722  retval(k++) = hchild;
1723  }
1724 
1725  return retval;
1726 }
1727 
1728 void
1730 {
1731  for (auto& hchild : children_list)
1732  {
1734 
1735  if (go.valid_object () && ! go.get_properties ().is_beingdeleted ())
1736  gh_manager::free (hchild);
1737  }
1738 
1739  if (clear)
1740  children_list.clear ();
1741 }
1742 
1743 bool
1745 {
1746  // case 1: empty matrix
1747  // case 2: function handle
1748  // case 3: string corresponding to known function name
1749  // case 4: string that can be eval()'ed
1750  // case 5: cell array with first element being a function handle
1751 
1752  if (v.isempty ())
1753  return true;
1754  else if (v.is_function_handle ())
1755  return true;
1756  else if (v.is_string ())
1757  // complete validation will be done at execution-time
1758  return true;
1759  else if (v.iscell () && (v.rows () == 1 || v.columns () == 1)
1760  && v.cell_value ()(0).is_function_handle ())
1761  return true;
1762 
1763  return false;
1764 }
1765 
1767 {
1768 public:
1769 
1770  callback_props (void) : m_set () { }
1771 
1772  callback_props (const callback_props&) = delete;
1773 
1774  callback_props& operator = (const callback_props&) = delete;
1775 
1776  ~callback_props (void) = default;
1777 
1778  bool empty (void) const { return m_set.empty (); }
1779 
1780  void insert (const callback_property *ptr)
1781  {
1782  m_set.insert (reinterpret_cast<intptr_t> (ptr));
1783  }
1784 
1785  void erase (const callback_property *ptr)
1786  {
1787  m_set.erase (reinterpret_cast<intptr_t> (ptr));
1788  }
1789 
1790  bool contains (const callback_property *ptr) const
1791  {
1792  return m_set.find (reinterpret_cast<intptr_t> (ptr)) != m_set.end ();
1793  }
1794 
1795 private:
1796 
1797  std::set<intptr_t> m_set;
1798 };
1799 
1800 // Elements of this set are pointers to currently executing
1801 // callback_property objects. Used to determine handle visibility
1802 // inside callback functions.
1803 
1805 
1806 void
1808 {
1810 
1811  // We are executing a callback function, so allow handles that have
1812  // their handlevisibility property set to "callback" to be visible.
1813 
1815 
1816  if (! executing_callbacks.contains (this))
1817  {
1818  executing_callbacks.insert (this);
1819 
1820  if (callback.is_defined () && ! callback.isempty ())
1822  }
1823 }
1824 
1825 // Used to cache dummy graphics objects from which dynamic properties can be
1826 // cloned.
1827 static std::map<caseless_str, graphics_object> dprop_obj_map;
1828 
1829 property
1831  const caseless_str& type, const octave_value_list& args)
1832 {
1833  property retval;
1834 
1835  if (type.compare ("string"))
1836  {
1837  std::string sv = (args.length () > 0 ? args(0).string_value () : "");
1838 
1839  retval = property (new string_property (name, h, sv));
1840  }
1841  else if (type.compare ("any"))
1842  {
1843  octave_value ov = (args.length () > 0 ? args(0)
1844  : octave_value (Matrix ()));
1845 
1846  retval = property (new any_property (name, h, ov));
1847  }
1848  else if (type.compare ("radio"))
1849  {
1850  if (args.length () < 1)
1851  error ("addproperty: missing possible values for radio property");
1852 
1853  std::string sv = args(0).xstring_value ("addproperty: argument for radio property must be a string");
1854 
1855  retval = property (new radio_property (name, h, sv));
1856 
1857  if (args.length () > 1)
1858  retval.set (args(1));
1859  }
1860  else if (type.compare ("double"))
1861  {
1862  double dv = (args.length () > 0 ? args(0).double_value () : 0.0);
1863 
1864  retval = property (new double_property (name, h, dv));
1865  }
1866  else if (type.compare ("handle"))
1867  {
1868  double hv = (args.length () > 0 ? args(0).double_value ()
1870 
1871  graphics_handle gh (hv);
1872 
1873  retval = property (new handle_property (name, h, gh));
1874  }
1875  else if (type.compare ("boolean"))
1876  {
1877  retval = property (new bool_property (name, h, false));
1878 
1879  if (args.length () > 0)
1880  retval.set (args(0));
1881  }
1882  else if (type.compare ("data"))
1883  {
1884  retval = property (new array_property (name, h, Matrix ()));
1885 
1886  if (args.length () > 0)
1887  {
1888  retval.set (args(0));
1889  // FIXME: additional argument could define constraints,
1890  // but is this really useful?
1891  }
1892  }
1893  else if (type.compare ("color"))
1894  {
1895  color_values cv (0, 0, 0);
1896  radio_values rv;
1897 
1898  if (args.length () > 1)
1899  rv = radio_values (args(1).string_value ());
1900 
1901  retval = property (new color_property (name, h, cv, rv));
1902 
1903  if (args.length () > 0 && ! args(0).isempty ())
1904  retval.set (args(0));
1905  else
1906  retval.set (rv.default_value ());
1907  }
1908  else
1909  {
1910  caseless_str go_name, go_rest;
1911 
1912  if (! lookup_object_name (type, go_name, go_rest))
1913  error ("addproperty: unsupported type for dynamic property (= %s)",
1914  type.c_str ());
1915 
1916  graphics_object go;
1917 
1918  std::map<caseless_str, graphics_object>::const_iterator it =
1919  dprop_obj_map.find (go_name);
1920 
1921  if (it == dprop_obj_map.end ())
1922  {
1923  base_graphics_object *bgo =
1925 
1926  if (bgo)
1927  {
1928  go = graphics_object (bgo);
1929 
1930  dprop_obj_map[go_name] = go;
1931  }
1932  }
1933  else
1934  go = it->second;
1935 
1936  if (! go.valid_object ())
1937  error ("addproperty: invalid object type (= %s)",
1938  go_name.c_str ());
1939 
1940  property prop = go.get_properties ().get_property (go_rest);
1941 
1942  retval = prop.clone ();
1943 
1944  retval.set_parent (h);
1945  retval.set_name (name);
1946 
1947  if (args.length () > 0)
1948  retval.set (args(0));
1949  }
1950 
1951  return retval;
1952 }
1953 
1954 static void
1956 {
1958 
1959  if (go)
1960  {
1961  Matrix children = go.get_properties ().get_all_children ();
1962 
1963  for (int k = 0; k < children.numel (); k++)
1964  finalize_r (children(k));
1965 
1966  go.finalize ();
1967  }
1968 }
1969 
1970 static void
1972 {
1974 
1975  if (go)
1976  {
1977  Matrix children = go.get_properties ().get_all_children ();
1978 
1979  go.initialize ();
1980 
1981  for (int k = 0; k < children.numel (); k++)
1982  initialize_r (children(k));
1983  }
1984 }
1985 
1986 void
1988 {
1989  if (toolkit)
1990  finalize_r (get___myhandle__ ());
1991 
1992  toolkit = b;
1993  __graphics_toolkit__ = b.get_name ();
1994  __plot_stream__ = Matrix ();
1995 
1996  if (toolkit)
1997  initialize_r (get___myhandle__ ());
1998 
1999  mark_modified ();
2000 }
2001 
2002 void
2003 figure::properties::set___mouse_mode__ (const octave_value& val_arg)
2004 {
2005  std::string direction = "in";
2006 
2007  octave_value val = val_arg;
2008 
2009  if (val.is_string ())
2010  {
2011  std::string modestr = val.string_value ();
2012 
2013  if (modestr == "zoom in")
2014  {
2015  val = modestr = "zoom";
2016  direction = "in";
2017  }
2018  else if (modestr == "zoom out")
2019  {
2020  val = modestr = "zoom";
2021  direction = "out";
2022  }
2023 
2024  if (__mouse_mode__.set (val, true))
2025  {
2026  std::string mode = __mouse_mode__.current_value ();
2027 
2028  octave_scalar_map pm = get___pan_mode__ ().scalar_map_value ();
2029  pm.setfield ("Enable", mode == "pan" ? "on" : "off");
2030  set___pan_mode__ (pm);
2031 
2032  octave_scalar_map rm = get___rotate_mode__ ().scalar_map_value ();
2033  rm.setfield ("Enable", mode == "rotate" ? "on" : "off");
2034  set___rotate_mode__ (rm);
2035 
2036  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
2037  zm.setfield ("Enable", mode == "zoom" ? "on" : "off");
2038  zm.setfield ("Direction", direction);
2039  set___zoom_mode__ (zm);
2040 
2041  mark_modified ();
2042  }
2043  else if (modestr == "zoom")
2044  {
2045  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
2046  std::string curr_direction
2047  = zm.getfield ("Direction").string_value ();
2048 
2049  if (direction != curr_direction)
2050  {
2051  zm.setfield ("Direction", direction);
2052  set___zoom_mode__ (zm);
2053 
2054  mark_modified ();
2055  }
2056  }
2057  }
2058 }
2059 
2060 void
2061 figure::properties::update_handlevisibility (void)
2062 {
2063  if (! is_handle_visible ())
2064  {
2065  octave_value cf = gh_manager::get_object (0).get ("currentfigure");
2066  if (! cf.isempty () && cf.double_value () == __myhandle__)
2067  {
2068  gh_manager::auto_lock guard;
2069  octave_value kids = gh_manager::get_object (0).get ("children");
2070  if (kids.isempty ())
2071  gh_manager::get_object (0).set ("currentfigure", Matrix ());
2072  else
2073  {
2074  NDArray kidsarray = kids.array_value ();
2075  gh_manager::get_object (0).set ("currentfigure", kidsarray(0));
2076  }
2077  }
2078  }
2079 
2080  base_properties::update_handlevisibility ();
2081 }
2082 // ---------------------------------------------------------------------
2083 
2084 void
2086 {
2087  size_t offset = 0;
2088 
2089  size_t len = name.length ();
2090 
2091  if (len > 4)
2092  {
2093  caseless_str pfx = name.substr (0, 4);
2094 
2095  if (pfx.compare ("axes") || pfx.compare ("line")
2096  || pfx.compare ("text"))
2097  offset = 4;
2098  else if (len > 5)
2099  {
2100  pfx = name.substr (0, 5);
2101 
2102  if (pfx.compare ("image") || pfx.compare ("patch"))
2103  offset = 5;
2104  else if (len > 6)
2105  {
2106  pfx = name.substr (0, 6);
2107 
2108  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2109  offset = 6;
2110  else if (len > 7)
2111  {
2112  pfx = name.substr (0, 7);
2113 
2114  if (pfx.compare ("surface") || pfx.compare ("hggroup")
2115  || pfx.compare ("uipanel"))
2116  offset = 7;
2117  else if (len > 9)
2118  {
2119  pfx = name.substr (0, 9);
2120 
2121  if (pfx.compare ("uicontrol")
2122  || pfx.compare ("uitoolbar"))
2123  offset = 9;
2124  else if (len > 10)
2125  {
2126  pfx = name.substr (0, 10);
2127 
2128  if (pfx.compare ("uipushtool"))
2129  offset = 10;
2130  else if (len > 12)
2131  {
2132  pfx = name.substr (0, 12);
2133 
2134  if (pfx.compare ("uitoogletool"))
2135  offset = 12;
2136  else if (len > 13)
2137  {
2138  pfx = name.substr (0, 13);
2139 
2140  if (pfx.compare ("uicontextmenu")
2141  || pfx.compare ("uibuttongroup"))
2142  offset = 13;
2143  }
2144  }
2145  }
2146  }
2147  }
2148  }
2149  }
2150 
2151  if (offset > 0)
2152  {
2153  // FIXME: should we validate property names and values here?
2154 
2155  std::string pname = name.substr (offset);
2156 
2157  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2158  std::transform (pname.begin (), pname.end (), pname.begin (),
2159  tolower);
2160 
2161  bool has_property = false;
2162  if (pfx == "axes")
2163  has_property = axes::properties::has_core_property (pname);
2164  else if (pfx == "figure")
2165  has_property = figure::properties::has_core_property (pname);
2166  else if (pfx == "line")
2167  has_property = line::properties::has_core_property (pname);
2168  else if (pfx == "text")
2169  has_property = text::properties::has_core_property (pname);
2170  else if (pfx == "image")
2171  has_property = image::properties::has_core_property (pname);
2172  else if (pfx == "patch")
2173  has_property = patch::properties::has_core_property (pname);
2174  else if (pfx == "surface")
2175  has_property = surface::properties::has_core_property (pname);
2176  else if (pfx == "hggroup")
2177  has_property = hggroup::properties::has_core_property (pname);
2178  else if (pfx == "uimenu")
2179  has_property = uimenu::properties::has_core_property (pname);
2180  else if (pfx == "uicontrol")
2181  has_property = uicontrol::properties::has_core_property (pname);
2182  else if (pfx == "uibuttongroup")
2183  has_property = uibuttongroup::properties::has_core_property (pname);
2184  else if (pfx == "uipanel")
2185  has_property = uipanel::properties::has_core_property (pname);
2186  else if (pfx == "uicontextmenu")
2187  has_property = uicontextmenu::properties::has_core_property (pname);
2188  else if (pfx == "uitoolbar")
2189  has_property = uitoolbar::properties::has_core_property (pname);
2190  else if (pfx == "uipushtool")
2191  has_property = uipushtool::properties::has_core_property (pname);
2192 
2193  if (! has_property)
2194  error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
2195 
2196  bool remove = false;
2197  if (val.is_string ())
2198  {
2199  std::string sval = val.string_value ();
2200 
2201  remove = (sval == "remove");
2202  }
2203 
2204  pval_map_type& pval_map = plist_map[pfx];
2205 
2206  if (remove)
2207  {
2208  pval_map_iterator p = pval_map.find (pname);
2209 
2210  if (p != pval_map.end ())
2211  pval_map.erase (p);
2212  }
2213  else
2214  pval_map[pname] = val;
2215  }
2216  }
2217 
2218  if (offset == 0)
2219  error ("invalid default property specification");
2220 }
2221 
2224 {
2226 
2227  size_t offset = 0;
2228 
2229  size_t len = name.length ();
2230 
2231  if (len > 4)
2232  {
2233  caseless_str pfx = name.substr (0, 4);
2234 
2235  if (pfx.compare ("axes") || pfx.compare ("line")
2236  || pfx.compare ("text"))
2237  offset = 4;
2238  else if (len > 5)
2239  {
2240  pfx = name.substr (0, 5);
2241 
2242  if (pfx.compare ("image") || pfx.compare ("patch"))
2243  offset = 5;
2244  else if (len > 6)
2245  {
2246  pfx = name.substr (0, 6);
2247 
2248  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2249  offset = 6;
2250  else if (len > 7)
2251  {
2252  pfx = name.substr (0, 7);
2253 
2254  if (pfx.compare ("surface") || pfx.compare ("hggroup")
2255  || pfx.compare ("uipanel"))
2256  offset = 7;
2257  else if (len > 9)
2258  {
2259  pfx = name.substr (0, 9);
2260 
2261  if (pfx.compare ("uicontrol")
2262  || pfx.compare ("uitoolbar"))
2263  offset = 9;
2264  else if (len > 10)
2265  {
2266  pfx = name.substr (0, 10);
2267 
2268  if (pfx.compare ("uipushtool"))
2269  offset = 10;
2270  else if (len > 12)
2271  {
2272  pfx = name.substr (0, 12);
2273 
2274  if (pfx.compare ("uitoggletool"))
2275  offset = 12;
2276  else if (len > 13)
2277  {
2278  pfx = name.substr (0, 13);
2279 
2280  if (pfx.compare ("uicontextmenu")
2281  || pfx.compare ("uibuttongroup"))
2282  offset = 13;
2283  }
2284  }
2285  }
2286  }
2287  }
2288  }
2289  }
2290 
2291  if (offset > 0)
2292  {
2293  std::string pname = name.substr (offset);
2294 
2295  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2296  std::transform (pname.begin (), pname.end (), pname.begin (),
2297  tolower);
2298 
2300 
2301  if (p != end ())
2302  {
2303  const pval_map_type& pval_map = p->second;
2304 
2305  pval_map_const_iterator q = pval_map.find (pname);
2306 
2307  if (q != pval_map.end ())
2308  retval = q->second;
2309  }
2310  }
2311  }
2312 
2313  return retval;
2314 }
2315 
2317 property_list::as_struct (const std::string& prefix_arg) const
2318 {
2320 
2321  for (plist_map_const_iterator p = begin (); p != end (); p++)
2322  {
2323  std::string prefix = prefix_arg + p->first;
2324 
2325  for (const auto& prop_val : p->second)
2326  m.assign (prefix + prop_val.first, prop_val.second);
2327  }
2328 
2329  return m;
2330 }
2331 
2332 // Set properties given as a cs-list of name, value pairs.
2333 
2334 void
2336 {
2337  int nargin = args.length ();
2338 
2339  if (nargin == 0)
2340  error ("graphics_object::set: Nothing to set");
2341 
2342  for (int i = 0; i < nargin; )
2343  {
2344  if (args(i).isstruct () )
2345  {
2346  set (args(i).map_value ());
2347  i++;
2348  }
2349  else if (i < nargin - 1)
2350  {
2351  caseless_str pname = args(i).xstring_value ("set: argument %d must be a property name", i);
2352  octave_value val = args(i+1);
2353  set_value_or_default (pname, val);
2354  i += 2;
2355  }
2356  else
2357  error ("set: invalid number of arguments");
2358  }
2359 }
2360 
2361 /*
2362 ## test set with name, value pairs
2363 %!test
2364 %! hf = figure ("visible", "off");
2365 %! h = plot (1:10, 10:-1:1);
2366 %! set (h, "linewidth", 10, "marker", "x");
2367 %! lw = get (h, "linewidth");
2368 %! mk = get (h, "marker");
2369 %! close (hf);
2370 %! assert (lw, 10);
2371 %! assert (mk, "x");
2372 */
2373 
2374 // Set properties given in two cell arrays containing names and values.
2375 void
2377  const Cell& values, octave_idx_type row)
2378 {
2379  if (pnames.numel () != values.columns ())
2380  error ("set: number of names must match number of value columns (%d != %d)",
2381  pnames.numel (), values.columns ());
2382 
2383  octave_idx_type k = pnames.columns ();
2384 
2385  for (octave_idx_type column = 0; column < k; column++)
2386  {
2387  caseless_str pname = pnames(column);
2389 
2390  set_value_or_default (pname, val);
2391  }
2392 }
2393 
2394 /*
2395 ## test set with cell array arguments
2396 %!test
2397 %! hf = figure ("visible", "off");
2398 %! h = plot (1:10, 10:-1:1);
2399 %! set (h, {"linewidth", "marker"}, {10, "x"});
2400 %! lw = get (h, "linewidth");
2401 %! mk = get (h, "marker");
2402 %! close (hf);
2403 %! assert (lw, 10);
2404 %! assert (mk, "x");
2405 
2406 ## test set with multiple handles and cell array arguments
2407 %!test
2408 %! hf = figure ("visible", "off");
2409 %! unwind_protect
2410 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2411 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
2412 %! assert (get (h, "linewidth"), {10; 5});
2413 %! assert (get (h, "marker"), {"x"; "o"});
2414 %! set (h, {"linewidth", "marker"}, {10, "x"});
2415 %! assert (get (h, "linewidth"), {10; 10});
2416 %! assert (get (h, "marker"), {"x"; "x"});
2417 %! unwind_protect_cleanup
2418 %! close (hf);
2419 %! end_unwind_protect;
2420 
2421 %!error <set: number of graphics handles must match number of value rows>
2422 %! hf = figure ("visible", "off");
2423 %! unwind_protect
2424 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2425 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
2426 %! unwind_protect_cleanup
2427 %! close (hf);
2428 %! end_unwind_protect
2429 
2430 %!error <set: number of names must match number of value columns>
2431 %! hf = figure ("visible", "off");
2432 %! unwind_protect
2433 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2434 %! set (h, {"linewidth"}, {10, "x"; 5, "o"});
2435 %! unwind_protect_cleanup
2436 %! close (hf);
2437 %! end_unwind_protect
2438 */
2439 
2440 // Set properties given in a struct array
2441 void
2443 {
2444  for (octave_idx_type p = 0; p < m.nfields (); p++)
2445  {
2446  // FIXME: Would it be better to extract all the keys at once rather than
2447  // repeatedly call keys() inside a for loop?
2448  caseless_str pname = m.keys ()[p];
2449 
2450  octave_value val = octave_value (m.contents (pname).elem (m.numel () - 1));
2451 
2452  set_value_or_default (pname, val);
2453  }
2454 }
2455 
2456 /*
2457 ## test set ticklabels for compatibility
2458 %!test
2459 %! hf = figure ("visible", "off");
2460 %! set (gca (), "xticklabel", [0, 0.2, 0.4, 0.6, 0.8, 1]);
2461 %! xticklabel = get (gca (), "xticklabel");
2462 %! close (hf);
2463 %! assert (class (xticklabel), "char");
2464 %! assert (size (xticklabel), [6, 3]);
2465 
2466 %!test
2467 %! hf = figure ("visible", "off");
2468 %! set (gca (), "xticklabel", "0|0.2|0.4|0.6|0.8|1");
2469 %! xticklabel = get (gca (), "xticklabel");
2470 %! close (hf);
2471 %! assert (class (xticklabel), "char");
2472 %! assert (size (xticklabel), [6, 3]);
2473 
2474 %!test
2475 %! hf = figure ("visible", "off");
2476 %! set (gca (), "xticklabel", ["0 "; "0.2"; "0.4"; "0.6"; "0.8"; "1 "]);
2477 %! xticklabel = get (gca (), "xticklabel");
2478 %! close (hf);
2479 %! assert (class (xticklabel), "char");
2480 %! assert (size (xticklabel), [6, 3]);
2481 
2482 %!test
2483 %! hf = figure ("visible", "off");
2484 %! set (gca (), "xticklabel", {"0", "0.2", "0.4", "0.6", "0.8", "1"});
2485 %! xticklabel = get (gca (), "xticklabel");
2486 %! close (hf);
2487 %! assert (class (xticklabel), "cell");
2488 %! assert (size (xticklabel), [6, 1]);
2489 */
2490 
2491 /*
2492 ## test set with struct arguments
2493 %!test
2494 %! hf = figure ("visible", "off");
2495 %! unwind_protect
2496 %! h = plot (1:10, 10:-1:1);
2497 %! set (h, struct ("linewidth", 10, "marker", "x"));
2498 %! assert (get (h, "linewidth"), 10);
2499 %! assert (get (h, "marker"), "x");
2500 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2501 %! set (h, struct ("linewidth", {5, 10}));
2502 %! assert (get (h, "linewidth"), {10; 10});
2503 %! unwind_protect_cleanup
2504 %! close (hf);
2505 %! end_unwind_protect
2506 
2507 ## test ordering
2508 %!test
2509 %! markchanged = @(h, foobar, name) set (h, "userdata", [get(h,"userdata"); {name}]);
2510 %! hf = figure ("visible", "off");
2511 %! unwind_protect
2512 %! h = line ();
2513 %! set (h, "userdata", {});
2514 %! addlistener (h, "color", {markchanged, "color"});
2515 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2516 %! ## "linewidth" first
2517 %! props.linewidth = 2;
2518 %! props.color = "r";
2519 %! set (h, props);
2520 %! assert (get (h, "userdata"), fieldnames (props));
2521 %! clear props;
2522 %! clf ();
2523 %! h = line ();
2524 %! set (h, "userdata", {});
2525 %! addlistener (h, "color", {markchanged, "color"});
2526 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2527 %! ## "color" first
2528 %! props.color = "r";
2529 %! props.linewidth = 2;
2530 %! set (h, props);
2531 %! assert (get (h, "userdata"), fieldnames (props));
2532 %! unwind_protect_cleanup
2533 %! close (hf);
2534 %! end_unwind_protect
2535 */
2536 
2537 // Set a property to a value or to its (factory) default value.
2538 
2539 void
2541  const octave_value& val)
2542 {
2543  if (val.is_string ())
2544  {
2545  std::string sval = val.string_value ();
2546 
2547  octave_value default_val;
2548 
2549  if (sval == "default")
2550  {
2551  default_val = get_default (pname);
2552 
2553  rep->set (pname, default_val);
2554  }
2555  else if (sval == "factory")
2556  {
2557  default_val = get_factory_default (pname);
2558 
2559  rep->set (pname, default_val);
2560  }
2561  else
2562  {
2563  // Matlab specifically uses "\default" to escape string setting
2564  if (sval == R"(\default)")
2565  rep->set (pname, "default");
2566  else if (sval == R"(\factory)")
2567  rep->set (pname, "factory");
2568  else
2569  rep->set (pname, val);
2570  }
2571  }
2572  else
2573  rep->set (pname, val);
2574 }
2575 
2576 /*
2577 ## test setting of default values
2578 %!test
2579 %! old_lw = get (0, "defaultlinelinewidth");
2580 %! unwind_protect
2581 %! hf = figure ("visible", "off");
2582 %! h = plot (1:10, 10:-1:1);
2583 %! set (0, "defaultlinelinewidth", 20);
2584 %! set (h, "linewidth", "default");
2585 %! assert (get (h, "linewidth"), 20);
2586 %! set (h, "linewidth", "factory");
2587 %! assert (get (h, "linewidth"), 0.5);
2588 %! unwind_protect_cleanup
2589 %! close (hf);
2590 %! set (0, "defaultlinelinewidth", old_lw);
2591 %! end_unwind_protect
2592 */
2593 
2594 static double
2596 {
2597  static double maxrand = RAND_MAX + 2.0;
2598 
2599  return (rand () + 1.0) / maxrand;
2600 }
2601 
2603 gh_manager::do_get_handle (bool integer_figure_handle)
2604 {
2606 
2607  if (integer_figure_handle)
2608  {
2609  // Figure handles are positive integers corresponding
2610  // to the figure number.
2611 
2612  // We always want the lowest unused figure number.
2613 
2614  retval = 1;
2615 
2616  while (handle_map.find (retval) != handle_map.end ())
2617  retval++;
2618  }
2619  else
2620  {
2621  // Other graphics handles are negative integers plus some random
2622  // fractional part. To avoid running out of integers, we recycle the
2623  // integer part but tack on a new random part each time.
2624 
2625  free_list_iterator p = handle_free_list.begin ();
2626 
2627  if (p != handle_free_list.end ())
2628  {
2629  retval = *p;
2630  handle_free_list.erase (p);
2631  }
2632  else
2633  {
2634  retval = graphics_handle (next_handle);
2635 
2636  next_handle = std::ceil (next_handle) - 1.0 - make_handle_fraction ();
2637  }
2638  }
2639 
2640  return retval;
2641 }
2642 
2643 void
2645 {
2646  if (h.ok ())
2647  {
2648  if (h.value () == 0)
2649  error ("graphics_handle::free: can't delete root figure");
2650 
2651  iterator p = handle_map.find (h);
2652 
2653  if (p == handle_map.end ())
2654  error ("graphics_handle::free: invalid object %g", h.value ());
2655 
2656  base_properties& bp = p->second.get_properties ();
2657 
2658  bp.set_beingdeleted (true);
2659 
2660  bp.delete_children ();
2661 
2662  octave_value val = bp.get_deletefcn ();
2663 
2664  bp.execute_deletefcn ();
2665 
2666  // Notify graphics toolkit.
2667  p->second.finalize ();
2668 
2669  // Note: this will be valid only for first explicitly deleted
2670  // object. All its children will then have an
2671  // unknown graphics toolkit.
2672 
2673  // Graphics handles for non-figure objects are negative
2674  // integers plus some random fractional part. To avoid
2675  // running out of integers, we recycle the integer part
2676  // but tack on a new random part each time.
2677 
2678  handle_map.erase (p);
2679 
2680  if (h.value () < 0)
2681  handle_free_list.insert
2682  (std::ceil (h.value ()) - make_handle_fraction ());
2683  }
2684 }
2685 
2686 void
2688  const graphics_handle& new_gh)
2689 {
2690  iterator p = handle_map.find (old_gh);
2691 
2692  if (p == handle_map.end ())
2693  error ("graphics_handle::free: invalid object %g", old_gh.value ());
2694 
2695  graphics_object go = p->second;
2696 
2697  handle_map.erase (p);
2698 
2699  handle_map[new_gh] = go;
2700 
2701  if (old_gh.value () < 0)
2702  handle_free_list.insert (std::ceil (old_gh.value ())
2703  - make_handle_fraction ());
2704 
2705  for (auto& hfig : figure_list)
2706  {
2707  if (hfig == old_gh)
2708  {
2709  hfig = new_gh;
2710  break;
2711  }
2712  }
2713 }
2714 
2715 gh_manager *gh_manager::instance = nullptr;
2716 
2717 static void
2719  const octave_value& val)
2720 {
2722  go.set (pname, val);
2723 }
2724 
2725 static void
2727 {
2728  if (args.length () > 0)
2729  {
2731  go.set (args);
2732  }
2733 }
2734 
2735 static octave_value
2737 {
2739  return go.get (pname);
2740 }
2741 
2742 static graphics_handle
2743 reparent (const octave_value& ov, const std::string& who,
2744  const std::string& pname, const graphics_handle& new_parent,
2745  bool adopt = true)
2746 {
2748 
2749  double hv = ov.xdouble_value ("%s: %s must be a graphics handle",
2750  who.c_str (), pname.c_str ());
2751 
2752  h = gh_manager::lookup (hv);
2753 
2754  if (! h.ok ())
2755  error ("%s: invalid graphics handle (= %g) for %s",
2756  who.c_str (), hv, pname.c_str ());
2757 
2759 
2760  graphics_handle parent_h = go.get_parent ();
2761 
2762  graphics_object parent_go = gh_manager::get_object (parent_h);
2763 
2764  parent_go.remove_child (h);
2765 
2766  if (adopt)
2767  go.set ("parent", new_parent.value ());
2768  else
2769  go.reparent (new_parent);
2770 
2771  return h;
2772 }
2773 
2774 // This function is NOT equivalent to the scripting language function gcf.
2776 gcf (void)
2777 {
2778  octave_value val = xget (0, "currentfigure");
2779 
2780  return val.isempty () ? octave::numeric_limits<double>::NaN ()
2781  : val.double_value ();
2782 }
2783 
2784 // This function is NOT equivalent to the scripting language function gca.
2786 gca (void)
2787 {
2788  octave_value val = xget (gcf (), "currentaxes");
2789 
2790  return val.isempty () ? octave::numeric_limits<double>::NaN ()
2791  : val.double_value ();
2792 }
2793 
2794 static void
2796 {
2797  if (h.ok ())
2798  {
2800 
2801  // Don't do recursive deleting, due to callbacks
2802  if (! go.get_properties ().is_beingdeleted ())
2803  {
2804  graphics_handle parent_h = go.get_parent ();
2805 
2806  graphics_object parent_go = gh_manager::get_object (parent_h);
2807 
2808  // NOTE: free the handle before removing it from its parent's
2809  // children, such that the object's state is correct when the
2810  // deletefcn callback is executed
2811 
2812  gh_manager::free (h);
2813 
2814  // A callback function might have already deleted the parent
2815  if (parent_go.valid_object ())
2816  parent_go.remove_child (h);
2817 
2818  Vdrawnow_requested = true;
2819  }
2820  }
2821 }
2822 
2823 static void
2825 {
2827 }
2828 
2829 // Flag to stop redraws due to callbacks while deletion is in progress.
2830 static bool delete_executing = false;
2831 
2832 static void
2834 {
2835  // Prevent redraw of partially deleted objects.
2838  delete_executing = true;
2839 
2840  for (octave_idx_type i = 0; i < vals.numel (); i++)
2841  delete_graphics_object (vals.elem (i));
2842 }
2843 
2844 static void
2846 {
2847  octave_value closerequestfcn = xget (h, "closerequestfcn");
2848 
2849  gh_manager::execute_callback (h, closerequestfcn);
2850 }
2851 
2852 static void
2854 {
2855  // Remove the deletefcn and closerequestfcn callbacks
2856  // and delete the object directly.
2857 
2858  xset (h, "deletefcn", Matrix ());
2859  xset (h, "closerequestfcn", Matrix ());
2860 
2862 }
2863 
2864 void
2866 {
2867  // FIXME: should we process or discard pending events?
2868 
2869  event_queue.clear ();
2870 
2871  // Don't use figure_list_iterator because we'll be removing elements
2872  // from the list elsewhere.
2873 
2874  Matrix hlist = do_figure_handle_list (true);
2875 
2876  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2877  {
2879 
2880  if (h.ok ())
2881  close_figure (h);
2882  }
2883 
2884  // They should all be closed now. If not, force them to close.
2885 
2886  hlist = do_figure_handle_list (true);
2887 
2888  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2889  {
2891 
2892  if (h.ok ())
2894  }
2895 
2896  // None left now, right?
2897 
2898  hlist = do_figure_handle_list (true);
2899 
2900  if (hlist.numel () != 0)
2901  warning ("gh_manager::do_close_all_figures: some graphics elements failed to close.");
2902 
2903  // Clear all callback objects from our list.
2904 
2905  callback_objects.clear ();
2906 }
2907 
2908 static void
2909 adopt (const graphics_handle& parent_h, const graphics_handle& h)
2910 {
2911  graphics_object parent_go = gh_manager::get_object (parent_h);
2912  parent_go.adopt (h);
2913 }
2914 
2915 static bool
2917 {
2918  return h.ok ();
2919 }
2920 
2921 static bool
2923 {
2925 
2926  return h.ok ();
2927 }
2928 
2929 static octave_value
2931 {
2932  octave_value retval = false;
2933 
2934  if (val.is_real_scalar () && is_hghandle (val.double_value ()))
2935  retval = true;
2936  else if (val.isnumeric () && val.isreal ())
2937  {
2938  const NDArray handles = val.array_value ();
2939 
2940  boolNDArray result (handles.dims ());
2941 
2942  for (octave_idx_type i = 0; i < handles.numel (); i++)
2943  result.xelem (i) = is_hghandle (handles(i));
2944 
2945  retval = result;
2946  }
2947 
2948  return retval;
2949 }
2950 
2951 static bool
2952 is_figure (double val)
2953 {
2955 
2956  return go && go.isa ("figure");
2957 }
2958 
2959 static void
2961 {
2963  go.get_properties ().execute_createfcn ();
2964 }
2965 
2966 static void
2968 {
2970 
2971  if (go)
2972  go.initialize ();
2973 }
2974 
2975 // ---------------------------------------------------------------------
2976 
2977 void
2979 {
2981 
2982  finalize (go);
2983 }
2984 
2985 static int
2986 toggle_warn (std::string id, bool on, int state = -1)
2987 {
2988  if (! on)
2989  {
2990  state = warning_enabled (id);
2991  disable_warning (id);
2992  }
2993  else
2994  {
2995  if (state == 1)
2996  set_warning_state (id, "on");
2997  else if (state == 2)
2998  set_warning_state (id, "error");
2999  }
3000  return state;
3001 }
3002 
3003 static void
3005  property_list::pval_map_type factory_pval)
3006 {
3008 
3009  // Replace factory defaults by user defined ones
3010  std::string go_name = go.get_properties ().graphics_object_name ();
3012  go.build_user_defaults_map (pval, go_name);
3013 
3014  for (const auto& p : pval)
3015  factory_pval[p.first] = p.second;
3016 
3017  // Save warning state of "Octave:deprecated-property"
3018  int state = toggle_warn ("Octave:deprecated-property", false);
3019 
3020  // Reset defaults
3021  for (const auto& p : factory_pval)
3022  {
3023  std::string pname = p.first;
3024 
3025  // Don't reset internal properties and handle_properties
3026  if (! go.has_readonly_property (pname)
3027  && pname.find ("__") != 0 && pname.find ("current") != 0
3028  && pname != "uicontextmenu" && pname != "parent")
3029  {
3030  // Store *mode prop/val in order to set them last
3031  if (pname.find ("mode") == (pname.length () - 4))
3032  pval[pname] = p.second;
3033  else
3034  go.set (pname, p.second);
3035  }
3036  }
3037 
3038  // set *mode properties
3039  for (const auto& p : pval)
3040  go.set (p.first, p.second);
3041 
3042  toggle_warn ("Octave:deprecated-property", true, state);
3043 }
3044 
3045 // ---------------------------------------------------------------------
3046 
3047 void
3050 {
3051  std::string go_name = graphics_object_name ();
3052 
3053  property_list::plist_map_const_iterator plist = defaults.find (go_name);
3054 
3055  if (plist != defaults.end ())
3056  {
3057  const property_list::pval_map_type pval_map = plist->second;
3058 
3059  for (const auto& prop_val : pval_map)
3060  {
3061  std::string pname = prop_val.first;
3062 
3063  try
3064  {
3065  bgo.set (pname, prop_val.second);
3066  }
3067  catch (octave::execution_exception& e)
3068  {
3069  error (e, "error setting default property %s", pname.c_str ());
3070  }
3071  }
3072  }
3073 }
3074 
3075 /*
3076 ## test defaults are set in the order they were stored
3077 %!test
3078 %! set(0, "defaultfigureunits", "normalized");
3079 %! set(0, "defaultfigureposition", [0.7 0 0.3 0.3]);
3080 %! hf = figure ("visible", "off");
3081 %! tol = 20 * eps;
3082 %! unwind_protect
3083 %! assert (get (hf, "position"), [0.7 0 0.3 0.3], tol);
3084 %! unwind_protect_cleanup
3085 %! close (hf);
3086 %! set(0, "defaultfigureunits", "remove");
3087 %! set(0, "defaultfigureposition", "remove");
3088 %! end_unwind_protect
3089 */
3090 
3093 {
3094  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
3095  all_props.find (pname);
3096 
3097  if (it == all_props.end ())
3098  error (R"(get: unknown property "%s")", pname.c_str ());
3099 
3100  return it->second.get ();
3101 }
3102 
3105 {
3107 
3108  for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator
3109  it = all_props.begin (); it != all_props.end (); ++it)
3110  if (all || ! it->second.is_hidden ())
3111  m.assign (it->second.get_name (), it->second.get ());
3112 
3113  return m;
3114 }
3115 
3116 std::set<std::string>
3118 {
3119  return dynamic_properties;
3120 }
3121 
3122 bool
3124 {
3125  const std::set<std::string>& dynprops = dynamic_property_names ();
3126 
3127  if (dynprops.find (pname) != dynprops.end ())
3128  return true;
3129  else
3130  return all_props.find (pname) != all_props.end ();
3131 }
3132 
3133 void
3135  const octave_value& val)
3136 {
3137  std::map<caseless_str, property, cmp_caseless_str>::iterator it =
3138  all_props.find (pname);
3139 
3140  if (it == all_props.end ())
3141  error (R"(set: unknown property "%s")", pname.c_str ());
3142 
3143  it->second.set (val);
3144 
3145  dynamic_properties.insert (pname);
3146 
3147  mark_modified ();
3148 }
3149 
3150 property
3152 {
3153  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
3154  all_props.find (pname);
3155 
3156  if (it == all_props.end ())
3157  error (R"(get_property: unknown property "%s")", pname.c_str ());
3158 
3159  return it->second;
3160 }
3161 
3162 void
3164 {
3165  double hp = val.xdouble_value ("set: parent must be a graphics handle");
3166 
3168 
3169  if (hp == __myhandle__)
3170  error ("set: can not set object parent to be object itself");
3171 
3172  new_parent = gh_manager::lookup (hp);
3173 
3174  if (! new_parent.ok ())
3175  error ("set: invalid graphics handle (= %g) for parent", hp);
3176 
3177  // Remove child from current parent
3178  graphics_object old_parent_go;
3179  old_parent_go = gh_manager::get_object (get_parent ());
3180 
3181  if (old_parent_go.get_handle () != hp)
3182  old_parent_go.remove_child (__myhandle__);
3183  else
3184  return; // Do nothing more
3185 
3186  // Check new parent's parent is not this child to avoid recursion
3187  graphics_object new_parent_go;
3188  new_parent_go = gh_manager::get_object (new_parent);
3189  if (new_parent_go.get_parent () == __myhandle__)
3190  {
3191  // new parent's parent gets child's original parent
3192  new_parent_go.get_properties ().set_parent (get_parent ().as_octave_value ());
3193  }
3194 
3195  // Set parent property to new_parent and do adoption
3196  parent = new_parent.as_octave_value ();
3197  ::adopt (parent.handle_value (), __myhandle__);
3198 }
3199 
3200 /*
3201 %!test
3202 %! hf = figure ("visible", "off");
3203 %! unwind_protect
3204 %! hax = gca ();
3205 %! set (hax, "parent", gcf ());
3206 %! assert (gca (), hax);
3207 %! unwind_protect_cleanup
3208 %! close (hf);
3209 %! end_unwind_protect
3210 */
3211 
3212 void
3214 {
3215  // Mark existing object as modified
3216  __modified__ = "on";
3217  // Attempt to mark parent object as modified if it exists
3219  if (parent_go)
3220  parent_go.mark_modified ();
3221 }
3222 
3223 void
3225 {
3227 
3228  if (parent_go)
3229  parent_go.override_defaults (obj);
3230 }
3231 
3232 void
3234 {
3235  graphics_object go = gh_manager::get_object (__myhandle__);
3236 
3237  if (go)
3238  go.update_axis_limits (axis_type);
3239 }
3240 
3241 void
3243  const graphics_handle& h) const
3244 {
3245  graphics_object go = gh_manager::get_object (__myhandle__);
3246 
3247  if (go)
3248  go.update_axis_limits (axis_type, h);
3249 }
3250 
3251 void
3253 {
3254  if (uicontextmenu.get ().isempty ())
3255  return;
3256 
3258  if (go && go.isa ("uicontextmenu"))
3259  {
3260  uicontextmenu::properties& props =
3261  reinterpret_cast<uicontextmenu::properties&> (go.get_properties ());
3262  props.add_dependent_obj (__myhandle__);
3263  }
3264 }
3265 
3266 bool
3268 {
3269  return (handlevisibility.is ("on")
3270  || (! executing_callbacks.empty () && ! handlevisibility.is ("off")));
3271 }
3272 
3275 {
3277 
3278  if (go)
3279  return go.get_toolkit ();
3280  else
3281  return graphics_toolkit ();
3282 }
3283 
3284 void
3286 {
3287  Matrix kids = get_children ();
3288 
3289  for (int i = 0; i < kids.numel (); i++)
3290  {
3292 
3293  if (go.valid_object ())
3295  }
3296 }
3297 
3298 void
3300 {
3302 
3303  if (parent_go.valid_object ())
3304  parent_go.get_properties ().update_autopos (elem_type);
3305 }
3306 
3307 void
3308 base_properties::update_handlevisibility (void)
3309 {
3310  if (is_handle_visible ())
3311  return;
3312 
3313  // This object should not be the root "callbackobject"
3315  octave_value cbo = rt.get ("callbackobject");
3316  if (! cbo.isempty () && cbo.double_value () == __myhandle__)
3317  {
3318  gh_manager::auto_lock guard;
3319  auto& root_props =
3320  dynamic_cast<root_figure::properties&> (rt.get_properties ());
3321  root_props.set_callbackobject (Matrix ());
3322  }
3323 
3324  // This object should not be the figure "currentobject"
3325  graphics_object go (gh_manager::get_object (get___myhandle__ ()));
3326  graphics_object fig (go.get_ancestor ("figure"));
3327  if (fig.valid_object ())
3328  {
3329  octave_value co = fig.get ("currentobject");
3330  if (! co.isempty () && co.double_value () == __myhandle__)
3331  {
3332  gh_manager::auto_lock guard;
3333  auto& fig_props =
3334  dynamic_cast<figure::properties&> (fig.get_properties ());
3335  fig_props.set_currentobject (Matrix ());
3336  }
3337  }
3338 }
3339 
3340 /*
3341 ## test current figure and current axes have visible handles
3342 %!test
3343 %! hf1 = figure ("visible", "off");
3344 %! hf2 = figure ("visible", "off");
3345 %! hax1 = axes ();
3346 %! hax2 = axes ();
3347 %! unwind_protect
3348 %! assert (get (0, "currentfigure"), hf2);
3349 %! assert (get (hf2, "currentaxes"), hax2);
3350 %! set (hf2, "handlevisibility", "off");
3351 %! assert (get (0, "currentfigure"), hf1);
3352 %! set (hax2, "handlevisibility", "off");
3353 %! assert (get (hf2, "currentaxes"), hax1);
3354 %! assert (get (hf2, "currentobject"), []);
3355 %! unwind_protect_cleanup
3356 %! close ([hf1, hf2]);
3357 %! end_unwind_protect;
3358 */
3359 
3360 /*
3361 ## test current callback object have visible handle
3362 %!test
3363 %! hf = figure ("visible", "off");
3364 %! hax = axes ();
3365 %! unwind_protect
3366 %! fcn = @(h) assert (gcbo (), h);
3367 %! addlistener (hax, "color", fcn);
3368 %! set (hax, "color", "r");
3369 %! dellistener (hax, "color", fcn);
3370 %! set (hax, "handlevisibility", "off");
3371 %! fcn = @() assert (gcbo (), []);
3372 %! addlistener (hax, "color", fcn);
3373 %! set (hax, "color", "b");
3374 %! unwind_protect_cleanup
3375 %! close (hf);
3376 %! end_unwind_protect;
3377 */
3378 
3379 void
3381  const octave_value& val,
3383 {
3384  property p = get_property (pname);
3385 
3386  if (p.ok ())
3387  p.add_listener (val, mode);
3388 }
3389 
3390 void
3392  const octave_value& val,
3394 {
3395  property p = get_property (pname);
3396 
3397  if (p.ok ())
3398  p.delete_listener (val, mode);
3399 }
3400 
3401 // ---------------------------------------------------------------------
3402 
3403 void
3405 {
3406  if (! valid_object ())
3407  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3408 
3410 
3411  if (parent_go)
3412  parent_go.update_axis_limits (axis_type);
3413 }
3414 
3415 void
3417  const graphics_handle& h)
3418 {
3419  if (! valid_object ())
3420  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3421 
3423 
3424  if (parent_go)
3425  parent_go.update_axis_limits (axis_type, h);
3426 }
3427 
3428 void
3430 {
3431  int state = toggle_warn ("Octave:deprecated-property", false);
3432  octave_map m = get (true).map_value ();
3433  toggle_warn ("Octave:deprecated-property", true, state);
3434 
3435  for (const auto& pm : m)
3436  {
3437  // FIXME: there has to be a better way. I think we want to
3438  // ask whether it is OK to delete the listener for the given
3439  // property. How can we know in advance that it will be OK?
3440 
3442 
3446 
3447  discard_error_messages = true;
3448  Vdebug_on_error = false;
3449  Vdebug_on_warning = false;
3450 
3451  try
3452  {
3453  property p = get_properties ().get_property (pm.first);
3454 
3455  if (p.ok ())
3456  p.delete_listener ();
3457  }
3458  catch (const octave::execution_exception&)
3459  {
3461  }
3462  }
3463 }
3464 
3465 void
3467 {
3468  property_list local_defaults = get_defaults_list ();
3469  const auto it = local_defaults.find (go_name);
3470 
3471  if (it != local_defaults.end ())
3472  {
3473  property_list::pval_map_type pval_lst = it->second;
3474  for (const auto& prop_val : pval_lst)
3475  {
3476  std::string pname = prop_val.first;
3477  if (def.find (pname) == def.end ())
3478  def[pname] = prop_val.second;
3479  }
3480  }
3481 
3483 
3484  if (parent_go)
3485  parent_go.build_user_defaults_map (def, go_name);
3486 }
3487 
3488 void
3490 {
3491  if (valid_object ())
3492  {
3493  property_list::pval_map_type factory_pval =
3495  .find (type ())->second;
3496 
3498  xreset_default_properties (get_handle (), factory_pval);
3499  }
3500 }
3501 
3504 {
3505  if (! valid_object ())
3506  error ("base_graphics_object::values_as_string: invalid graphics object");
3507 
3509  octave_map m = get ().map_value ();
3511 
3512  for (const auto& pm : m)
3513  {
3514  const auto& pname = pm.first;
3515  if (pname != "children" && ! go.has_readonly_property (pname))
3516  {
3517  property p = get_properties ().get_property (pname);
3518 
3519  if (p.ok () && ! p.is_hidden ())
3520  {
3521  retval += "\n\t" + std::string (pname) + ": ";
3522  if (p.is_radio ())
3523  retval += p.values_as_string ();
3524  }
3525  }
3526  }
3527 
3528  if (! retval.empty ())
3529  retval += '\n';
3530 
3531  return retval;
3532 }
3533 
3536 {
3538 
3539  if (! valid_object ())
3540  error ("base_graphics_object::value_as_string: invalid graphics object");
3541 
3543 
3544  if (prop != "children" && ! go.has_readonly_property (prop))
3545  {
3546  property p = get_properties ().get_property (prop);
3547 
3548  if (p.ok () && ! p.is_hidden ())
3549  {
3550  if (p.is_radio ())
3551  retval += p.values_as_string ();
3552  }
3553  }
3554 
3555  if (! retval.empty ())
3556  retval += '\n';
3557 
3558  return retval;
3559 }
3560 
3563 {
3565 
3566  if (! valid_object ())
3567  error ("base_graphics_object::values_as_struct: invalid graphics object");
3568 
3569  octave_scalar_map m = get ().scalar_map_value ();
3571 
3572  for (const auto& pm : m)
3573  {
3574  const auto& pname = pm.first;
3575  if (pname != "children" && ! go.has_readonly_property (pname))
3576  {
3577  property p = get_properties ().get_property (pname);
3578 
3579  if (p.ok () && ! p.is_hidden ())
3580  {
3581  if (p.is_radio ())
3582  retval.assign (p.get_name (), p.values_as_cell ());
3583  else
3584  retval.assign (p.get_name (), Cell ());
3585  }
3586  }
3587  }
3588 
3589  return retval;
3590 }
3591 
3592 /*
3593 %!test
3594 %! hfig = figure ("visible", "off");
3595 %! unwind_protect
3596 %! hax = axes ();
3597 %! ret = set (hax, "tightinset");
3598 %! assert (isempty (ret));
3599 %! ret = set (hax, "type");
3600 %! assert (isempty (ret));
3601 %! ret = set (hfig, "tag");
3602 %! assert (isempty (ret));
3603 %! ret = set (0, "commandwindowsize");
3604 %! assert (isempty (ret));
3605 %! ret = set (0);
3606 %! assert (! isfield (ret, "commandwindowsize"));
3607 %! unwind_protect_cleanup
3608 %! close (hfig);
3609 %! end_unwind_protect
3610 */
3611 
3614 {
3615  if (valid_object ())
3616  {
3617  if (isa (obj_type))
3618  return *this;
3619  else
3620  return gh_manager::get_object (get_parent ()).get_ancestor (obj_type);
3621  }
3622  else
3623  return graphics_object ();
3624 }
3625 
3626 // ---------------------------------------------------------------------
3627 
3628 #include "graphics-props.cc"
3629 
3630 // ---------------------------------------------------------------------
3631 
3632 void
3633 root_figure::properties::set_callbackobject (const octave_value& v)
3634 {
3635  graphics_handle val (v);
3636 
3637  if (octave::math::isnan (val.value ()))
3638  {
3639  if (! cbo_stack.empty ())
3640  {
3641  val = cbo_stack.front ();
3642 
3643  cbo_stack.pop_front ();
3644  }
3645 
3646  callbackobject = val;
3647  }
3648  else if (is_hghandle (val))
3649  {
3650  if (get_callbackobject ().ok ())
3651  cbo_stack.push_front (get_callbackobject ());
3652 
3653  callbackobject = val;
3654  }
3655  else
3656  err_set_invalid ("callbackobject");
3657 }
3658 
3659 void
3660 root_figure::properties::set_currentfigure (const octave_value& v)
3661 {
3662  graphics_handle val (v);
3663 
3664  if (octave::math::isnan (val.value ()) || is_hghandle (val))
3665  {
3666  currentfigure = val;
3667 
3668  if (val.ok ())
3670  }
3671  else
3672  err_set_invalid ("currentfigure");
3673 }
3674 
3675 void
3676 figure::properties::set_integerhandle (const octave_value& val)
3677 {
3678  if (integerhandle.set (val, true))
3679  {
3680  bool int_fig_handle = integerhandle.is_on ();
3681 
3682  graphics_object this_go = gh_manager::get_object (__myhandle__);
3683 
3684  graphics_handle old_myhandle = __myhandle__;
3685 
3686  __myhandle__ = gh_manager::get_handle (int_fig_handle);
3687 
3688  gh_manager::renumber_figure (old_myhandle, __myhandle__);
3689 
3691 
3692  base_properties& props = parent_go.get_properties ();
3693 
3694  props.renumber_child (old_myhandle, __myhandle__);
3695 
3696  Matrix kids = get_children ();
3697 
3698  for (octave_idx_type i = 0; i < kids.numel (); i++)
3699  {
3701 
3702  kid.get_properties ().renumber_parent (__myhandle__);
3703  }
3704 
3706 
3707  if (__myhandle__ == cf)
3708  xset (0, "currentfigure", __myhandle__.value ());
3709 
3710  this_go.update (integerhandle.get_id ());
3711 
3712  mark_modified ();
3713  }
3714 }
3715 
3716 // FIXME: This should update monitorpositions and pointerlocation, but as these
3717 // properties aren't yet used, it doesn't matter that they aren't set either.
3718 void
3719 root_figure::properties::update_units (void)
3720 {
3721  std::string xunits = get_units ();
3722 
3723  Matrix scrn_sz = default_screensize ();
3724 
3725  double dpi = get_screenpixelsperinch ();
3726 
3727  if (xunits == "inches")
3728  {
3729  scrn_sz(0) = 0;
3730  scrn_sz(1) = 0;
3731  scrn_sz(2) /= dpi;
3732  scrn_sz(3) /= dpi;
3733  }
3734  else if (xunits == "centimeters")
3735  {
3736  scrn_sz(0) = 0;
3737  scrn_sz(1) = 0;
3738  scrn_sz(2) *= 2.54 / dpi;
3739  scrn_sz(3) *= 2.54 / dpi;
3740  }
3741  else if (xunits == "normalized")
3742  {
3743  scrn_sz = Matrix (1, 4, 1.0);
3744  scrn_sz(0) = 0;
3745  scrn_sz(1) = 0;
3746  }
3747  else if (xunits == "points")
3748  {
3749  scrn_sz(0) = 0;
3750  scrn_sz(1) = 0;
3751  scrn_sz(2) *= 72 / dpi;
3752  scrn_sz(3) *= 72 / dpi;
3753  }
3754 
3755  set_screensize (scrn_sz);
3756 }
3757 
3758 Matrix
3760 {
3761  Matrix screen_size = screen_size_pixels ();
3762  Matrix pos = Matrix (1, 4, 0.0);
3763 
3764  pos(2) = screen_size(0);
3765  pos(3) = screen_size(1);
3766 
3767  return pos;
3768 }
3769 
3770 /*
3771 %!test
3772 %! old_units = get (0, "units");
3773 %! unwind_protect
3774 %! set (0, "units", "pixels");
3775 %! sz = get (0, "screensize") - [1, 1, 0, 0];
3776 %! dpi = get (0, "screenpixelsperinch");
3777 %! set (0, "units", "inches");
3778 %! assert (get (0, "screensize"), sz / dpi, 0.5 / dpi);
3779 %! set (0, "units", "centimeters");
3780 %! assert (get (0, "screensize"), sz / dpi * 2.54, 0.5 / dpi * 2.54);
3781 %! set (0, "units", "points");
3782 %! assert (get (0, "screensize"), sz / dpi * 72, 0.5 / dpi * 72);
3783 %! set (0, "units", "normalized");
3784 %! assert (get (0, "screensize"), [0.0, 0.0, 1.0, 1.0]);
3785 %! set (0, "units", "pixels");
3786 %! assert (get (0, "screensize"), sz + [1, 1, 0, 0]);
3787 %! unwind_protect_cleanup
3788 %! set (0, "units", old_units);
3789 %! end_unwind_protect
3790 */
3791 
3792 void
3794 {
3796 
3798 
3799  xset (0, "currentfigure", cf.value ());
3800 
3802 }
3803 
3804 void
3806 {
3807  // empty list of local defaults
3809 
3813 }
3814 
3815 // ---------------------------------------------------------------------
3816 
3817 void
3818 figure::properties::set_currentaxes (const octave_value& val)
3819 {
3820  graphics_handle hax (val);
3821 
3822  if (octave::math::isnan (hax.value ()) || is_hghandle (hax))
3823  currentaxes = hax;
3824  else
3825  err_set_invalid ("currentaxes");
3826 }
3827 
3828 void
3830 {
3832 
3833  if (h == currentaxes.handle_value ())
3834  {
3835  graphics_handle new_currentaxes;
3836 
3837  Matrix kids = get_children ();
3838 
3839  for (octave_idx_type i = 0; i < kids.numel (); i++)
3840  {
3841  graphics_handle kid = kids(i);
3842 
3844 
3845  if (go.isa ("axes"))
3846  {
3847  new_currentaxes = kid;
3848  break;
3849  }
3850  }
3851 
3852  currentaxes = new_currentaxes;
3853  }
3854 }
3855 
3858 {
3859  if (! toolkit)
3860  {
3861  octave::gtk_manager& gtk_mgr
3862  = octave::__get_gtk_manager__ ("figure::properties::get_toolkit");
3863 
3864  toolkit = gtk_mgr.get_toolkit ();
3865  }
3866 
3867  return toolkit;
3868 }
3869 
3870 void
3872 {
3873  if (! val.is_string ())
3874  error ("set___graphics_toolkit__ must be a string");
3875 
3876  std::string nm = val.string_value ();
3877 
3878  octave::gtk_manager& gtk_mgr
3879  = octave::__get_gtk_manager__ ("figure::properties::set___graphics_toolkit__");
3880 
3881  graphics_toolkit b = gtk_mgr.find_toolkit (nm);
3882 
3883  if (b.get_name () != nm)
3884  error ("set___graphics_toolkit__: invalid graphics toolkit");
3885 
3886  if (nm != get___graphics_toolkit__ ())
3887  {
3888  set_toolkit (b);
3889  mark_modified ();
3890  }
3891 }
3892 
3893 void
3895 {
3897 
3898  if (! get_currentaxes ().ok ())
3899  {
3901 
3902  if (go.type () == "axes")
3903  set_currentaxes (h.as_octave_value ());
3904  }
3905 }
3906 
3907 /*
3908 %!test
3909 %! hf1 = figure ("visible", "off");
3910 %! ax1 = subplot (1,2,1);
3911 %! ax2 = subplot (1,2,2);
3912 %! hf2 = figure ("visible", "off");
3913 %! unwind_protect
3914 %! set (ax2, "parent", hf2);
3915 %! assert (get (hf2, "currentaxes"), ax2);
3916 %! assert (get (hf1, "currentaxes"), ax1);
3917 %! set (ax1, "parent", hf2);
3918 %! assert (get (hf2, "currentaxes"), ax2);
3919 %! unwind_protect_cleanup
3920 %! close (hf1);
3921 %! close (hf2);
3922 %! end_unwind_protect
3923 */
3924 
3925 void
3927 {
3928  std::string sval = val.string_value ();
3929 
3930  if (sval == "on")
3931  xset (0, "currentfigure", __myhandle__.value ());
3932 
3933  visible = val;
3934 }
3935 
3936 Matrix
3937 figure::properties::get_boundingbox (bool internal, const Matrix&) const
3938 {
3939  Matrix screen_size = screen_size_pixels ();
3940  Matrix pos = (internal ?
3941  get_position ().matrix_value () :
3942  get_outerposition ().matrix_value ());
3943 
3944  pos = convert_position (pos, get_units (), "pixels", screen_size);
3945 
3946  pos(0)--;
3947  pos(1)--;
3948  pos(1) = screen_size(1) - pos(1) - pos(3);
3949 
3950  return pos;
3951 }
3952 
3953 void
3955  bool do_notify_toolkit)
3956 {
3957  Matrix screen_size = screen_size_pixels ();
3958  Matrix pos = bb;
3959 
3960  pos(1) = screen_size(1) - pos(1) - pos(3);
3961  pos(1)++;
3962  pos(0)++;
3963  pos = convert_position (pos, "pixels", get_units (), screen_size);
3964 
3965  if (internal)
3966  set_position (pos, do_notify_toolkit);
3967  else
3968  set_outerposition (pos, do_notify_toolkit);
3969 }
3970 
3971 Matrix
3973 {
3974  Matrix bb = get_boundingbox (true);
3975  Matrix pos (1, 2, 0.0);
3976 
3977  pos(0) = x;
3978  pos(1) = y;
3979 
3980  pos(1) = bb(3) - pos(1);
3981  pos(0)++;
3982  pos = convert_position (pos, "pixels", get_units (),
3983  bb.extract_n (0, 2, 1, 2));
3984 
3985  return pos;
3986 }
3987 
3988 Matrix
3990 {
3991  Matrix bb = get_boundingbox (true);
3992  Matrix pos (1, 2, 0.0);
3993 
3994  pos(0) = x;
3995  pos(1) = y;
3996 
3997  pos = convert_position (pos, get_units (), "pixels",
3998  bb.extract_n (0, 2, 1, 2));
3999  pos(0)--;
4000  pos(1) = bb(3) - pos(1);
4001 
4002  return pos;
4003 }
4004 
4005 void
4007  bool do_notify_toolkit)
4008 {
4009  Matrix old_bb, new_bb;
4010  bool modified = false;
4011 
4012  old_bb = get_boundingbox (true);
4013  modified = position.set (v, false, do_notify_toolkit);
4014  new_bb = get_boundingbox (true);
4015 
4016  if (old_bb != new_bb)
4017  {
4018  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
4019  {
4020  execute_resizefcn ();
4021  update_boundingbox ();
4022  }
4023  }
4024 
4025  if (modified)
4026  {
4027  position.run_listeners (POSTSET);
4028  mark_modified ();
4029  }
4030 
4031  if (paperpositionmode.is ("auto"))
4032  paperposition.set (get_auto_paperposition ());
4033 }
4034 
4035 void
4037  bool do_notify_toolkit)
4038 {
4039  if (outerposition.set (v, true, do_notify_toolkit))
4040  mark_modified ();
4041 }
4042 
4043 void
4044 figure::properties::set_paperunits (const octave_value& val)
4045 {
4046  caseless_str punits = val.string_value ();
4047  caseless_str ptype = get_papertype ();
4048 
4049  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
4050  error ("set: can't set paperunits to normalized when papertype is custom");
4051 
4052  caseless_str old_paperunits = get_paperunits ();
4053  if (paperunits.set (val, true))
4054  {
4055  update_paperunits (old_paperunits);
4056  mark_modified ();
4057  }
4058 }
4059 
4060 void
4061 figure::properties::set_papertype (const octave_value& val)
4062 {
4063  caseless_str ptype = val.string_value ();
4064  caseless_str punits = get_paperunits ();
4065 
4066  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
4067  error ("set: can't set paperunits to normalized when papertype is custom");
4068 
4069  if (papertype.set (val, true))
4070  {
4071  update_papertype ();
4072  mark_modified ();
4073  }
4074 }
4075 
4076 static Matrix
4077 papersize_from_type (const caseless_str punits, const caseless_str ptype)
4078 {
4079  Matrix retval (1, 2, 1.0);
4080 
4081  if (! punits.compare ("normalized"))
4082  {
4083  double in2units;
4084  double mm2units;
4085 
4086  if (punits.compare ("inches"))
4087  {
4088  in2units = 1.0;
4089  mm2units = 1 / 25.4;
4090  }
4091  else if (punits.compare ("centimeters"))
4092  {
4093  in2units = 2.54;
4094  mm2units = 1 / 10.0;
4095  }
4096  else // points
4097  {
4098  in2units = 72.0;
4099  mm2units = 72.0 / 25.4;
4100  }
4101 
4102  if (ptype.compare ("usletter"))
4103  {
4104  retval(0) = 8.5 * in2units;
4105  retval(1) = 11.0 * in2units;
4106  }
4107  else if (ptype.compare ("uslegal"))
4108  {
4109  retval(0) = 8.5 * in2units;
4110  retval(1) = 14.0 * in2units;
4111  }
4112  else if (ptype.compare ("tabloid"))
4113  {
4114  retval(0) = 11.0 * in2units;
4115  retval(1) = 17.0 * in2units;
4116  }
4117  else if (ptype.compare ("a0"))
4118  {
4119  retval(0) = 841.0 * mm2units;
4120  retval(1) = 1189.0 * mm2units;
4121  }
4122  else if (ptype.compare ("a1"))
4123  {
4124  retval(0) = 594.0 * mm2units;
4125  retval(1) = 841.0 * mm2units;
4126  }
4127  else if (ptype.compare ("a2"))
4128  {
4129  retval(0) = 420.0 * mm2units;
4130  retval(1) = 594.0 * mm2units;
4131  }
4132  else if (ptype.compare ("a3"))
4133  {
4134  retval(0) = 297.0 * mm2units;
4135  retval(1) = 420.0 * mm2units;
4136  }
4137  else if (ptype.compare ("a4"))
4138  {
4139  retval(0) = 210.0 * mm2units;
4140  retval(1) = 297.0 * mm2units;
4141  }
4142  else if (ptype.compare ("a5"))
4143  {
4144  retval(0) = 148.0 * mm2units;
4145  retval(1) = 210.0 * mm2units;
4146  }
4147  else if (ptype.compare ("b0"))
4148  {
4149  retval(0) = 1029.0 * mm2units;
4150  retval(1) = 1456.0 * mm2units;
4151  }
4152  else if (ptype.compare ("b1"))
4153  {
4154  retval(0) = 728.0 * mm2units;
4155  retval(1) = 1028.0 * mm2units;
4156  }
4157  else if (ptype.compare ("b2"))
4158  {
4159  retval(0) = 514.0 * mm2units;
4160  retval(1) = 728.0 * mm2units;
4161  }
4162  else if (ptype.compare ("b3"))
4163  {
4164  retval(0) = 364.0 * mm2units;
4165  retval(1) = 514.0 * mm2units;
4166  }
4167  else if (ptype.compare ("b4"))
4168  {
4169  retval(0) = 257.0 * mm2units;
4170  retval(1) = 364.0 * mm2units;
4171  }
4172  else if (ptype.compare ("b5"))
4173  {
4174  retval(0) = 182.0 * mm2units;
4175  retval(1) = 257.0 * mm2units;
4176  }
4177  else if (ptype.compare ("arch-a"))
4178  {
4179  retval(0) = 9.0 * in2units;
4180  retval(1) = 12.0 * in2units;
4181  }
4182  else if (ptype.compare ("arch-b"))
4183  {
4184  retval(0) = 12.0 * in2units;
4185  retval(1) = 18.0 * in2units;
4186  }
4187  else if (ptype.compare ("arch-c"))
4188  {
4189  retval(0) = 18.0 * in2units;
4190  retval(1) = 24.0 * in2units;
4191  }
4192  else if (ptype.compare ("arch-d"))
4193  {
4194  retval(0) = 24.0 * in2units;
4195  retval(1) = 36.0 * in2units;
4196  }
4197  else if (ptype.compare ("arch-e"))
4198  {
4199  retval(0) = 36.0 * in2units;
4200  retval(1) = 48.0 * in2units;
4201  }
4202  else if (ptype.compare ("a"))
4203  {
4204  retval(0) = 8.5 * in2units;
4205  retval(1) = 11.0 * in2units;
4206  }
4207  else if (ptype.compare ("b"))
4208  {
4209  retval(0) = 11.0 * in2units;
4210  retval(1) = 17.0 * in2units;
4211  }
4212  else if (ptype.compare ("c"))
4213  {
4214  retval(0) = 17.0 * in2units;
4215  retval(1) = 22.0 * in2units;
4216  }
4217  else if (ptype.compare ("d"))
4218  {
4219  retval(0) = 22.0 * in2units;
4220  retval(1) = 34.0 * in2units;
4221  }
4222  else if (ptype.compare ("e"))
4223  {
4224  retval(0) = 34.0 * in2units;
4225  retval(1) = 43.0 * in2units;
4226  }
4227  }
4228 
4229  return retval;
4230 }
4231 
4232 Matrix
4233 figure::properties::get_auto_paperposition (void)
4234 {
4235  Matrix pos = get_position ().matrix_value ();
4236  Matrix sz;
4237 
4238  caseless_str funits = get_units ();
4239  caseless_str punits = get_paperunits ();
4240 
4241  // Convert position from figure units to paperunits
4242  if (funits == "normalized" || punits == "normalized")
4243  {
4244  sz = screen_size_pixels ();
4245  pos = convert_position (pos, funits, "inches", sz);
4246 
4247  if (punits == "normalized")
4248  sz = papersize_from_type ("points", get_papertype ());
4249 
4250  pos = convert_position (pos, "inches", punits, sz);
4251  }
4252  else
4253  pos = convert_position (pos, funits, punits, sz);
4254 
4255  // Center the figure on the page
4256  sz = get_papersize ().matrix_value ();
4257 
4258  pos(0) = sz(0)/2 - pos(2)/2;
4259  pos(1) = sz(1)/2 - pos(3)/2;
4260 
4261  return pos;
4262 }
4263 
4264 /*
4265 %!test
4266 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4267 %! in_pos = [0 0 4 5];
4268 %! tol = 20 * eps ();
4269 %! unwind_protect
4270 %! ## paperpositionmode "auto" converts figure size to paper units
4271 %! set (hf, "units", "inches");
4272 %! set (hf, "position", in_pos);
4273 %! set (hf, "paperunits", "centimeters");
4274 %! psz = get (hf, "papersize");
4275 %! fsz = in_pos(3:4) * 2.54;
4276 %! pos = [(psz/2 .- fsz/2) fsz];
4277 %! set (hf, "paperpositionmode", "auto");
4278 %! assert (get (hf, "paperposition"), pos, tol);
4279 %! unwind_protect_cleanup
4280 %! close (hf);
4281 %! end_unwind_protect
4282 
4283 %!test
4284 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4285 %! in_pos = [0 0 4 5];
4286 %! tol = 20 * eps ();
4287 %! unwind_protect
4288 %! ## likewise with normalized units
4289 %! set (hf, "units", "inches");
4290 %! set (hf, "position", in_pos);
4291 %! psz = get (hf, "papersize");
4292 %! set (hf, "paperunits", "normalized");
4293 %! fsz = in_pos(3:4) ./ psz;
4294 %! pos = [([0.5 0.5] .- fsz/2) fsz];
4295 %! assert (get (hf, "paperposition"), pos, tol);
4296 %! unwind_protect_cleanup
4297 %! close (hf);
4298 %! end_unwind_protect
4299 
4300 %!test
4301 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4302 %! in_pos = [0 0 4 5];
4303 %! tol = 20 * eps ();
4304 %! unwind_protect
4305 %! ## changing papertype updates paperposition
4306 %! set (hf, "units", "inches");
4307 %! set (hf, "position", in_pos);
4308 %! set (hf, "papertype", "a4");
4309 %! psz = get (hf, "papersize");
4310 %! fsz = in_pos(3:4);
4311 %! pos = [(psz/2 .- fsz/2) fsz];
4312 %! assert (get (hf, "paperposition"), pos, tol);
4313 %! unwind_protect_cleanup
4314 %! close (hf);
4315 %! end_unwind_protect
4316 
4317 %!test
4318 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4319 %! in_pos = [0 0 4 5];
4320 %! tol = 20 * eps ();
4321 %! unwind_protect
4322 %! ## lanscape updates paperposition
4323 %! set (hf, "units", "inches");
4324 %! set (hf, "position", in_pos);
4325 %! set (hf, "paperorientation", "landscape");
4326 %! psz = get (hf, "papersize");
4327 %! fsz = in_pos(3:4);
4328 %! pos = [(psz/2 .- fsz/2) fsz];
4329 %! assert (get (hf, "paperposition"), pos, tol);
4330 %! unwind_protect_cleanup
4331 %! close (hf);
4332 %! end_unwind_protect
4333 
4334 %!test
4335 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4336 %! in_pos = [0 0 4 5];
4337 %! unwind_protect
4338 %! ## back to manual mode
4339 %! set (hf, "paperposition", in_pos * 1.1);
4340 %! assert (get (hf, "paperpositionmode"), "manual");
4341 %! assert (get (hf, "paperposition"), in_pos * 1.1);
4342 %! unwind_protect_cleanup
4343 %! close (hf);
4344 %! end_unwind_protect
4345 */
4346 
4347 void
4349 {
4350  Matrix pos = get_paperposition ().matrix_value ();
4351  Matrix sz = get_papersize ().matrix_value ();
4352 
4353  pos(0) /= sz(0);
4354  pos(1) /= sz(1);
4355  pos(2) /= sz(0);
4356  pos(3) /= sz(1);
4357 
4358  std::string porient = get_paperorientation ();
4359  caseless_str punits = get_paperunits ();
4360  caseless_str ptype = get_papertype ();
4361 
4362  if (ptype.compare ("<custom>"))
4363  {
4364  if (old_paperunits.compare ("centimeters"))
4365  {
4366  sz(0) /= 2.54;
4367  sz(1) /= 2.54;
4368  }
4369  else if (old_paperunits.compare ("points"))
4370  {
4371  sz(0) /= 72.0;
4372  sz(1) /= 72.0;
4373  }
4374 
4375  if (punits.compare ("centimeters"))
4376  {
4377  sz(0) *= 2.54;
4378  sz(1) *= 2.54;
4379  }
4380  else if (punits.compare ("points"))
4381  {
4382  sz(0) *= 72.0;
4383  sz(1) *= 72.0;
4384  }
4385  }
4386  else
4387  {
4388  sz = papersize_from_type (punits, ptype);
4389  if (porient == "landscape")
4390  std::swap (sz(0), sz(1));
4391  }
4392 
4393  pos(0) *= sz(0);
4394  pos(1) *= sz(1);
4395  pos(2) *= sz(0);
4396  pos(3) *= sz(1);
4397 
4398  papersize.set (octave_value (sz));
4399  paperposition.set (octave_value (pos));
4400 }
4401 
4402 void
4403 figure::properties::update_papertype (void)
4404 {
4405  std::string typ = get_papertype ();
4406  if (typ != "<custom>")
4407  {
4408  Matrix sz = papersize_from_type (get_paperunits (), typ);
4409  if (get_paperorientation () == "landscape")
4410  std::swap (sz(0), sz(1));
4411  // Call papersize.set rather than set_papersize to avoid loops
4412  // between update_papersize and update_papertype.
4413  papersize.set (octave_value (sz));
4414  }
4415 
4416  if (paperpositionmode.is ("auto"))
4417  paperposition.set (get_auto_paperposition ());
4418 }
4419 
4420 void
4421 figure::properties::update_papersize (void)
4422 {
4423  Matrix sz = get_papersize ().matrix_value ();
4424  if (sz(0) > sz(1))
4425  {
4426  std::swap (sz(0), sz(1));
4427  papersize.set (octave_value (sz));
4428  paperorientation.set (octave_value ("landscape"));
4429  }
4430  else
4431  {
4432  paperorientation.set ("portrait");
4433  }
4434 
4435  std::string punits = get_paperunits ();
4436  if (punits == "centimeters")
4437  {
4438  sz(0) /= 2.54;
4439  sz(1) /= 2.54;
4440  }
4441  else if (punits == "points")
4442  {
4443  sz(0) /= 72.0;
4444  sz(1) /= 72.0;
4445  }
4446  if (punits == "normalized")
4447  {
4448  if (get_papertype () == "<custom>")
4449  error ("set: can't set the papertype to <custom> when the paperunits is normalized");
4450  }
4451  else
4452  {
4453  // FIXME: The papersizes info is also in papersize_from_type().
4454  // Both should be rewritten to avoid the duplication.
4455  // Don't Repeat Yourself (DRY) principle.
4456  std::string ptype = "<custom>";
4457  const double mm2in = 1.0 / 25.4;
4458  const double tol = 0.01;
4459 
4460  if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
4461  ptype = "usletter";
4462  else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 14.0) < tol)
4463  ptype = "uslegal";
4464  else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
4465  ptype = "tabloid";
4466  else if (std::abs (sz(0) - 841.0 * mm2in)
4467  + std::abs (sz(1) - 1198.0 * mm2in) < tol)
4468  ptype = "a0";
4469  else if (std::abs (sz(0) - 594.0 * mm2in)
4470  + std::abs (sz(1) - 841.0 * mm2in) < tol)
4471  ptype = "a1";
4472  else if (std::abs (sz(0) - 420.0 * mm2in)
4473  + std::abs (sz(1) - 594.0 * mm2in) < tol)
4474  ptype = "a2";
4475  else if (std::abs (sz(0) - 297.0 * mm2in)
4476  + std::abs (sz(1) - 420.0 * mm2in) < tol)
4477  ptype = "a3";
4478  else if (std::abs (sz(0) - 210.0 * mm2in)
4479  + std::abs (sz(1) - 297.0 * mm2in) < tol)
4480  ptype = "a4";
4481  else if (std::abs (sz(0) - 148.0 * mm2in)
4482  + std::abs (sz(1) - 210.0 * mm2in) < tol)
4483  ptype = "a5";
4484  else if (std::abs (sz(0) - 1029.0 * mm2in)
4485  + std::abs (sz(1) - 1456.0 * mm2in) < tol)
4486  ptype = "b0";
4487  else if (std::abs (sz(0) - 728.0 * mm2in)
4488  + std::abs (sz(1) - 1028.0 * mm2in) < tol)
4489  ptype = "b1";
4490  else if (std::abs (sz(0) - 514.0 * mm2in)
4491  + std::abs (sz(1) - 728.0 * mm2in) < tol)
4492  ptype = "b2";
4493  else if (std::abs (sz(0) - 364.0 * mm2in)
4494  + std::abs (sz(1) - 514.0 * mm2in) < tol)
4495  ptype = "b3";
4496  else if (std::abs (sz(0) - 257.0 * mm2in)
4497  + std::abs (sz(1) - 364.0 * mm2in) < tol)
4498  ptype = "b4";
4499  else if (std::abs (sz(0) - 182.0 * mm2in)
4500  + std::abs (sz(1) - 257.0 * mm2in) < tol)
4501  ptype = "b5";
4502  else if (std::abs (sz(0) - 9.0)
4503  + std::abs (sz(1) - 12.0) < tol)
4504  ptype = "arch-a";
4505  else if (std::abs (sz(0) - 12.0)
4506  + std::abs (sz(1) - 18.0) < tol)
4507  ptype = "arch-b";
4508  else if (std::abs (sz(0) - 18.0)
4509  + std::abs (sz(1) - 24.0) < tol)
4510  ptype = "arch-c";
4511  else if (std::abs (sz(0) - 24.0)
4512  + std::abs (sz(1) - 36.0) < tol)
4513  ptype = "arch-d";
4514  else if (std::abs (sz(0) - 36.0)
4515  + std::abs (sz(1) - 48.0) < tol)
4516  ptype = "arch-e";
4517  else if (std::abs (sz(0) - 8.5)
4518  + std::abs (sz(1) - 11.0) < tol)
4519  ptype = "a";
4520  else if (std::abs (sz(0) - 11.0)
4521  + std::abs (sz(1) - 17.0) < tol)
4522  ptype = "b";
4523  else if (std::abs (sz(0) - 17.0)
4524  + std::abs (sz(1) - 22.0) < tol)
4525  ptype = "c";
4526  else if (std::abs (sz(0) - 22.0)
4527  + std::abs (sz(1) - 34.0) < tol)
4528  ptype = "d";
4529  else if (std::abs (sz(0) - 34.0)
4530  + std::abs (sz(1) - 43.0) < tol)
4531  ptype = "e";
4532  // Call papertype.set rather than set_papertype to avoid loops between
4533  // update_papersize and update_papertype
4534  papertype.set (ptype);
4535  }
4536  if (punits == "centimeters")
4537  {
4538  sz(0) *= 2.54;
4539  sz(1) *= 2.54;
4540  }
4541  else if (punits == "points")
4542  {
4543  sz(0) *= 72.0;
4544  sz(1) *= 72.0;
4545  }
4546  if (get_paperorientation () == "landscape")
4547  {
4548  std::swap (sz(0), sz(1));
4549  papersize.set (octave_value (sz));
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 %! set (hf, "paperunits", "inches");
4561 %! set (hf, "papersize", [5, 4]);
4562 %! set (hf, "paperunits", "points");
4563 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4564 %! papersize = get (hf, "papersize");
4565 %! set (hf, "papersize", papersize + 1);
4566 %! set (hf, "papersize", papersize);
4567 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4568 %! unwind_protect_cleanup
4569 %! close (hf);
4570 %! end_unwind_protect
4571 
4572 %!test
4573 %! hf = figure ("visible", "off");
4574 %! unwind_protect
4575 %! set (hf, "paperunits", "inches");
4576 %! set (hf, "papersize", [5, 4]);
4577 %! set (hf, "paperunits", "centimeters");
4578 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4579 %! papersize = get (hf, "papersize");
4580 %! set (hf, "papersize", papersize + 1);
4581 %! set (hf, "papersize", papersize);
4582 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4583 %! unwind_protect_cleanup
4584 %! close (hf);
4585 %! end_unwind_protect
4586 */
4587 
4588 void
4589 figure::properties::update_paperorientation (void)
4590 {
4591  std::string porient = get_paperorientation ();
4592  Matrix sz = get_papersize ().matrix_value ();
4593  if ((sz(0) > sz(1) && porient == "portrait")
4594  || (sz(0) < sz(1) && porient == "landscape"))
4595  {
4596  std::swap (sz(0), sz(1));
4597  // Call papertype.set rather than set_papertype to avoid loops
4598  // between update_papersize and update_papertype
4599  papersize.set (octave_value (sz));
4600  }
4601 
4602  if (paperpositionmode.is ("auto"))
4603  paperposition.set (get_auto_paperposition ());
4604 }
4605 
4606 /*
4607 %!test
4608 %! hf = figure ("visible", "off");
4609 %! unwind_protect
4610 %! tol = 100 * eps ();
4611 %! ## UPPER case and MiXed case is part of test and should not be changed.
4612 %! set (hf, "paperorientation", "PORTRAIT");
4613 %! set (hf, "paperunits", "inches");
4614 %! set (hf, "papertype", "USletter");
4615 %! assert (get (hf, "papersize"), [8.5, 11.0], tol);
4616 %! set (hf, "paperorientation", "Landscape");
4617 %! assert (get (hf, "papersize"), [11.0, 8.5], tol);
4618 %! set (hf, "paperunits", "centimeters");
4619 %! assert (get (hf, "papersize"), [11.0, 8.5] * 2.54, tol);
4620 %! set (hf, "papertype", "a4");
4621 %! assert (get (hf, "papersize"), [29.7, 21.0], tol);
4622 %! set (hf, "paperunits", "inches", "papersize", [8.5, 11.0]);
4623 %! assert (get (hf, "papertype"), "usletter");
4624 %! assert (get (hf, "paperorientation"), "portrait");
4625 %! set (hf, "papersize", [11.0, 8.5]);
4626 %! assert (get (hf, "papertype"), "usletter");
4627 %! assert (get (hf, "paperorientation"), "landscape");
4628 %! unwind_protect_cleanup
4629 %! close (hf);
4630 %! end_unwind_protect
4631 */
4632 
4633 void
4634 figure::properties::set_units (const octave_value& val)
4635 {
4636  caseless_str old_units = get_units ();
4637 
4638  if (units.set (val, true))
4639  {
4640  update_units (old_units);
4641  mark_modified ();
4642  }
4643 }
4644 
4645 void
4647 {
4648  position.set (convert_position (get_position ().matrix_value (), old_units,
4649  get_units (), screen_size_pixels ()), false);
4650 }
4651 
4652 /*
4653 %!test
4654 %! hf = figure ("visible", "off");
4655 %! old_units = get (0, "units");
4656 %! unwind_protect
4657 %! set (0, "units", "pixels");
4658 %! rsz = get (0, "screensize");
4659 %! set (gcf (), "units", "pixels");
4660 %! fsz = get (gcf (), "position");
4661 %! set (gcf (), "units", "normalized");
4662 %! pos = get (gcf (), "position");
4663 %! assert (pos, (fsz - [1, 1, 0, 0]) ./ rsz([3, 4, 3, 4]));
4664 %! unwind_protect_cleanup
4665 %! close (hf);
4666 %! set (0, "units", old_units);
4667 %! end_unwind_protect
4668 */
4669 
4672 {
4673  if (is_numbertitle ())
4674  {
4675  std::ostringstream os;
4676  std::string nm = get_name ();
4677 
4678  os << "Figure " << __myhandle__.value ();
4679  if (! nm.empty ())
4680  os << ": " << get_name ();
4681 
4682  return os.str ();
4683  }
4684  else
4685  return get_name ();
4686 }
4687 
4690 {
4692 
4693  if (retval.is_undefined ())
4694  {
4695  graphics_handle parent_h = get_parent ();
4696  graphics_object parent_go = gh_manager::get_object (parent_h);
4697 
4698  retval = parent_go.get_default (name);
4699  }
4700 
4701  return retval;
4702 }
4703 
4704 void
4706 {
4707  // empty list of local defaults
4710 
4711  plist.erase ("units");
4712  plist.erase ("position");
4713  plist.erase ("outerposition");
4714  plist.erase ("paperunits");
4715  plist.erase ("paperposition");
4716  plist.erase ("windowstyle");
4717 
4720 }
4721 
4722 // ---------------------------------------------------------------------
4723 
4724 void
4725 axes::properties::init (void)
4726 {
4727  position.add_constraint (dim_vector (1, 4));
4728  outerposition.add_constraint (dim_vector (1, 4));
4729  tightinset.add_constraint (dim_vector (1, 4));
4730  looseinset.add_constraint (dim_vector (1, 4));
4731  colororder.add_constraint (dim_vector (-1, 3));
4732  dataaspectratio.add_constraint (3);
4733  dataaspectratio.add_constraint ("min", 0, false);
4734  dataaspectratio.add_constraint (FINITE);
4735  plotboxaspectratio.add_constraint (3);
4736  plotboxaspectratio.add_constraint ("min", 0, false);
4737  plotboxaspectratio.add_constraint (FINITE);
4738  // FIXME: Should these use dimension vectors? Currently can set 'xlim' to
4739  // any matrix size, but only first two elements are used.
4740  alim.add_constraint (2);
4741  alim.add_constraint (NOT_NAN);
4742  clim.add_constraint (2);
4743  clim.add_constraint (NOT_NAN);
4744  xlim.add_constraint (2);
4745  xlim.add_constraint (NOT_NAN);
4746  ylim.add_constraint (2);
4747  ylim.add_constraint (NOT_NAN);
4748  zlim.add_constraint (2);
4749  zlim.add_constraint (NOT_NAN);
4750  xtick.add_constraint (dim_vector (1, -1));
4751  xtick.add_constraint (FINITE);
4752  ytick.add_constraint (dim_vector (1, -1));
4753  ytick.add_constraint (FINITE);
4754  ztick.add_constraint (dim_vector (1, -1));
4755  ztick.add_constraint (FINITE);
4756  ticklength.add_constraint (dim_vector (1, 2));
4757  Matrix vw (1, 2, 0);
4758  vw(1) = 90;
4759  view = vw;
4760  view.add_constraint (dim_vector (1, 2));
4761  cameraposition.add_constraint (3);
4762  cameraposition.add_constraint (FINITE);
4763  cameratarget.add_constraint (3);
4764  cameratarget.add_constraint (FINITE);
4765  Matrix upv (1, 3, 0.0);
4766  upv(2) = 1.0;
4767  cameraupvector = upv;
4768  cameraupvector.add_constraint (3);
4769  cameraupvector.add_constraint (FINITE);
4770  cameraviewangle.add_constraint (FINITE);
4771  currentpoint.add_constraint (dim_vector (2, 3));
4772 
4773  // Range constraints for double properties
4774  fontsize.add_constraint ("min", 0.0, false);
4775  gridalpha.add_constraint ("min", 0.0, true);
4776  gridalpha.add_constraint ("max", 1.0, true);
4777  labelfontsizemultiplier.add_constraint ("min", 0.0, false);
4778  linewidth.add_constraint ("min", 0.0, false);
4779  minorgridalpha.add_constraint ("min", 0.0, true);
4780  minorgridalpha.add_constraint ("max", 1.0, true);
4781  titlefontsizemultiplier.add_constraint ("min", 0.0, false);
4782 
4783  // No constraints for hidden transform properties
4784  update_font ();
4785 
4786  x_zlim.resize (1, 2);
4787 
4788  sx = "linear";
4789  sy = "linear";
4790  sz = "linear";
4791 
4792  calc_ticklabels (xtick, xticklabel, xscale.is ("log"),
4793  xaxislocation_is ("origin"),
4794  yscale.is ("log") ? 2 : (yaxislocation_is ("origin") ? 0 :
4795  (yaxislocation_is ("left") ? -1 : 1)), xlim);
4796  calc_ticklabels (ytick, yticklabel, yscale.is ("log"),
4797  yaxislocation_is ("origin"),
4798  xscale.is ("log") ? 2 : (xaxislocation_is ("origin") ? 0 :
4799  (xaxislocation_is ("bottom") ? -1 : 1)), ylim);
4800  calc_ticklabels (ztick, zticklabel, zscale.is ("log"), false, 2, zlim);
4801 
4802  xset (xlabel.handle_value (), "handlevisibility", "off");
4803  xset (ylabel.handle_value (), "handlevisibility", "off");
4804  xset (zlabel.handle_value (), "handlevisibility", "off");
4805  xset (title.handle_value (), "handlevisibility", "off");
4806 
4807  xset (xlabel.handle_value (), "horizontalalignment", "center");
4808  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4809  xset (ylabel.handle_value (), "horizontalalignment", "center");
4810  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4811  xset (zlabel.handle_value (), "horizontalalignment", "right");
4812  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4813  xset (title.handle_value (), "horizontalalignment", "center");
4814  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4815 
4816  xset (xlabel.handle_value (), "verticalalignment", "top");
4817  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4818  xset (ylabel.handle_value (), "verticalalignment", "bottom");
4819  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4820  xset (title.handle_value (), "verticalalignment", "bottom");
4821  xset (title.handle_value (), "verticalalignmentmode", "auto");
4822 
4823  xset (ylabel.handle_value (), "rotation", 90.0);
4824  xset (ylabel.handle_value (), "rotationmode", "auto");
4825 
4826  xset (zlabel.handle_value (), "visible", "off");
4827 
4828  xset (xlabel.handle_value (), "clipping", "off");
4829  xset (ylabel.handle_value (), "clipping", "off");
4830  xset (zlabel.handle_value (), "clipping", "off");
4831  xset (title.handle_value (), "clipping", "off");
4832 
4833  xset (xlabel.handle_value (), "__autopos_tag__", "xlabel");
4834  xset (ylabel.handle_value (), "__autopos_tag__", "ylabel");
4835  xset (zlabel.handle_value (), "__autopos_tag__", "zlabel");
4836  xset (title.handle_value (), "__autopos_tag__", "title");
4837 
4838  double fs = labelfontsizemultiplier.double_value () *
4839  fontsize.double_value ();
4840  xset (xlabel.handle_value (), "fontsize", octave_value (fs));
4841  xset (ylabel.handle_value (), "fontsize", octave_value (fs));
4842  xset (zlabel.handle_value (), "fontsize", octave_value (fs));
4843  fs = titlefontsizemultiplier.double_value () * fontsize.double_value ();
4844  xset (title.handle_value (), "fontsize", octave_value (fs));
4845  xset (title.handle_value (), "fontweight", titlefontweight.get ());
4846 
4847  adopt (xlabel.handle_value ());
4848  adopt (ylabel.handle_value ());
4849  adopt (zlabel.handle_value ());
4850  adopt (title.handle_value ());
4851 
4852  Matrix tlooseinset = default_axes_position ();
4853  tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
4854  tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
4855  looseinset = tlooseinset;
4856 }
4857 
4858 /*
4859 ## Test validation of axes double properties range
4860 %!test
4861 %! hf = figure ("visible", "off");
4862 %! unwind_protect
4863 %! hax = axes ("parent", hf);
4864 %! try
4865 %! set (hax, "linewidth", -1);
4866 %! catch
4867 %! err = lasterr ();
4868 %! end_try_catch
4869 %! assert (err, 'set: "linewidth" must be greater than 0');
4870 %! try
4871 %! set (hax, "minorgridalpha", 1.5);
4872 %! catch
4873 %! err = lasterr ();
4874 %! end_try_catch
4875 %! assert (err, 'set: "minorgridalpha" must be less than or equal to 1');
4876 %! unwind_protect_cleanup
4877 %! delete (hf);
4878 %! end_unwind_protect
4879 */
4880 
4881 Matrix
4883 {
4884  Matrix pos = init_pos;
4886  Matrix parent_bb = go.get_properties ().get_boundingbox (true);
4887  Matrix ext = get_extent (true, true);
4888  ext(1) = parent_bb(3) - ext(1) - ext(3);
4889  ext(0)++;
4890  ext(1)++;
4891  ext = convert_position (ext, "pixels", get_units (),
4892  parent_bb.extract_n (0, 2, 1, 2));
4893  if (ext(0) < pos(0))
4894  {
4895  pos(2) += pos(0)-ext(0);
4896  pos(0) = ext(0);
4897  }
4898  if (ext(0)+ext(2) > pos(0)+pos(2))
4899  pos(2) = ext(0)+ext(2)-pos(0);
4900 
4901  if (ext(1) < pos(1))
4902  {
4903  pos(3) += pos(1)-ext(1);
4904  pos(1) = ext(1);
4905  }
4906  if (ext(1)+ext(3) > pos(1)+pos(3))
4907  pos(3) = ext(1)+ext(3)-pos(1);
4908 
4909  return pos;
4910 }
4911 
4912 void
4914 {
4915  // First part is equivalent to `update_tightinset ()'
4916  if (activepositionproperty.is ("position"))
4917  update_position ();
4918  else
4919  update_outerposition ();
4920  caseless_str old_units = get_units ();
4921  set_units ("normalized");
4922  Matrix pos = position.get ().matrix_value ();
4923  Matrix outpos = outerposition.get ().matrix_value ();
4924  Matrix tightpos = calc_tightbox (pos);
4925  Matrix tinset (1, 4, 1.0);
4926  tinset(0) = pos(0)-tightpos(0);
4927  tinset(1) = pos(1)-tightpos(1);
4928  tinset(2) = tightpos(0)+tightpos(2)-pos(0)-pos(2);
4929  tinset(3) = tightpos(1)+tightpos(3)-pos(1)-pos(3);
4930  tightinset = tinset;
4931  set_units (old_units);
4932  update_transform ();
4933  if (activepositionproperty.is ("position"))
4934  update_position ();
4935  else
4936  update_outerposition ();
4937 }
4938 
4939 /*
4940 %!testif HAVE_OPENGL, HAVE_FLTK; have_window_system
4941 %! hf = figure ("visible", "off");
4942 %! graphics_toolkit (hf, "fltk");
4943 %! unwind_protect
4944 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4945 %! hax = findall (gcf (), "type", "axes");
4946 %! positions = cell2mat (get (hax, "position"));
4947 %! outerpositions = cell2mat (get (hax, "outerposition"));
4948 %! looseinsets = cell2mat (get (hax, "looseinset"));
4949 %! tightinsets = cell2mat (get (hax, "tightinset"));
4950 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4951 %! hax = findall (gcf (), "type", "axes");
4952 %! assert (cell2mat (get (hax, "position")), positions, 1e-4);
4953 %! assert (cell2mat (get (hax, "outerposition")), outerpositions, 1e-4);
4954 %! assert (cell2mat (get (hax, "looseinset")), looseinsets, 1e-4);
4955 %! assert (cell2mat (get (hax, "tightinset")), tightinsets, 1e-4);
4956 %! unwind_protect_cleanup
4957 %! close (hf);
4958 %! end_unwind_protect
4959 
4960 %!testif HAVE_OPENGL, HAVE_FLTK; have_window_system
4961 %! hf = figure ("visible", "off");
4962 %! graphics_toolkit (hf, "fltk");
4963 %! fpos = get (hf, "position");
4964 %! unwind_protect
4965 %! plot (rand (3));
4966 %! position = get (gca, "position");
4967 %! outerposition = get (gca, "outerposition");
4968 %! looseinset = get (gca, "looseinset");
4969 %! tightinset = get (gca, "tightinset");
4970 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
4971 %! set (hf, "position", fpos);
4972 %! assert (get (gca, "outerposition"), outerposition, 0.001);
4973 %! assert (get (gca, "position"), position, 0.001);
4974 %! assert (get (gca, "looseinset"), looseinset, 0.001);
4975 %! assert (get (gca, "tightinset"), tightinset, 0.001);
4976 %! unwind_protect_cleanup
4977 %! close (hf);
4978 %! end_unwind_protect
4979 
4980 %!testif HAVE_OPENGL, HAVE_FLTK; have_window_system
4981 %! hf = figure ("visible", "off");
4982 %! graphics_toolkit (hf, "fltk");
4983 %! fpos = get (hf, "position");
4984 %! set (gca, "activepositionproperty", "position");
4985 %! unwind_protect
4986 %! plot (rand (3));
4987 %! position = get (gca, "position");
4988 %! outerposition = get (gca, "outerposition");
4989 %! looseinset = get (gca, "looseinset");
4990 %! tightinset = get (gca, "tightinset");
4991 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
4992 %! set (hf, "position", fpos);
4993 %! assert (get (gca, "position"), position, 0.001);
4994 %! assert (get (gca, "outerposition"), outerposition, 0.001);
4995 %! assert (get (gca, "looseinset"), looseinset, 0.001);
4996 %! assert (get (gca, "tightinset"), tightinset, 0.001);
4997 %! unwind_protect_cleanup
4998 %! close (hf);
4999 %! end_unwind_protect
5000 */
5001 
5002 void
5004  const std::string& who,
5005  const octave_value& v)
5006 {
5007  if (v.is_string ())
5008  {
5009  xset (hp.handle_value (), "string", v);
5010  return;
5011  }
5012 
5015 
5016  if (go.isa ("text"))
5017  val = ::reparent (v, "set", who, __myhandle__, false);
5018  else
5019  {
5020  std::string cname = v.class_name ();
5021 
5022  error ("set: expecting text graphics object or character string for %s property, found %s",
5023  who.c_str (), cname.c_str ());
5024  }
5025 
5026  xset (val, "handlevisibility", "off");
5027 
5029 
5031 
5032  hp = val;
5033 
5034  adopt (hp.handle_value ());
5035 }
5036 
5037 void
5038 axes::properties::set_xlabel (const octave_value& v)
5039 {
5040  set_text_child (xlabel, "xlabel", v);
5041  xset (xlabel.handle_value (), "positionmode", "auto");
5042  xset (xlabel.handle_value (), "rotationmode", "auto");
5043  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
5044  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
5045  xset (xlabel.handle_value (), "clipping", "off");
5046  xset (xlabel.handle_value (), "color", get_xcolor ());
5047  xset (xlabel.handle_value (), "__autopos_tag__", "xlabel");
5048  update_xlabel_position ();
5049 }
5050 
5051 void
5052 axes::properties::set_ylabel (const octave_value& v)
5053 {
5054  set_text_child (ylabel, "ylabel", v);
5055  xset (ylabel.handle_value (), "positionmode", "auto");
5056  xset (ylabel.handle_value (), "rotationmode", "auto");
5057  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
5058  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
5059  xset (ylabel.handle_value (), "clipping", "off");
5060  xset (ylabel.handle_value (), "color", get_ycolor ());
5061  xset (ylabel.handle_value (), "__autopos_tag__", "ylabel");
5062  update_ylabel_position ();
5063 }
5064 
5065 void
5066 axes::properties::set_zlabel (const octave_value& v)
5067 {
5068  set_text_child (zlabel, "zlabel", v);
5069  xset (zlabel.handle_value (), "positionmode", "auto");
5070  xset (zlabel.handle_value (), "rotationmode", "auto");
5071  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
5072  xset (zlabel.handle_value (), "verticalalignmentmode", "auto");
5073  xset (zlabel.handle_value (), "clipping", "off");
5074  xset (zlabel.handle_value (), "color", get_zcolor ());
5075  xset (zlabel.handle_value (), "__autopos_tag__", "zlabel");
5076  update_zlabel_position ();
5077 }
5078 
5079 void
5080 axes::properties::set_title (const octave_value& v)
5081 {
5082  set_text_child (title, "title", v);
5083  xset (title.handle_value (), "positionmode", "auto");
5084  xset (title.handle_value (), "horizontalalignment", "center");
5085  xset (title.handle_value (), "horizontalalignmentmode", "auto");
5086  xset (title.handle_value (), "verticalalignment", "bottom");
5087  xset (title.handle_value (), "verticalalignmentmode", "auto");
5088  xset (title.handle_value (), "clipping", "off");
5089  xset (title.handle_value (), "__autopos_tag__", "title");
5090  update_title_position ();
5091 }
5092 
5093 void
5095  const std::string& mode)
5096 {
5097  // FIXME: Should this have all properties in it?
5098  // Including ones we do don't implement?
5099 
5100  // FIXME: This function is probably never called without mode == "reset"
5101  // Check that this is the case with an assert statement (1/6/2017).
5102  // If there are reports of problems then figure out what code is
5103  // calling it with the mode set to something else.
5104  assert (mode == "reset");
5105 
5106  Matrix tlim (1, 2, 0.0);
5107  tlim(1) = 1;
5108  alim = tlim;
5109  xlim = tlim;
5110  ylim = tlim;
5111  zlim = tlim;
5112 
5113  alimmode = "auto";
5114  climmode = "auto";
5115  xlimmode = "auto";
5116  ylimmode = "auto";
5117  zlimmode = "auto";
5118 
5119  ambientlightcolor = Matrix (1, 3, 1.0);
5120 
5121  box = "off";
5122  boxstyle = "back";
5123 
5124  // Note: camera properties (not mode) will be set in update_transform
5125  camerapositionmode = "auto";
5126  cameratargetmode = "auto";
5127  cameraupvectormode = "auto";
5128  cameraviewanglemode = "auto";
5129 
5130  Matrix cl (1, 2, 0.0);
5131  cl(1) = 1;
5132  clim = cl;
5133 
5134  clippingstyle = "3dbox";
5135 
5136  color = color_values ("white");
5137  colororder = default_colororder ();
5138  colororderindex = 1.0;
5139 
5140  // Note: dataspectratio (not mode) will be set through update_aspectratios
5141  dataaspectratiomode = "auto";
5142 
5143  drawmode = "normal";
5144 
5145  fontangle = "normal";
5146  fontname = OCTAVE_DEFAULT_FONTNAME;
5147  fontsize = 10;
5148  fontunits = "points";
5149  fontsmoothing = "on";
5150  fontweight = "normal";
5151 
5152  gridalpha = 0.15;
5153  gridalphamode = "auto";
5154  gridcolor = color_values (0.15, 0.15, 0.15);
5155  gridcolormode = "auto";
5156  gridlinestyle = "-";
5157 
5158  labelfontsizemultiplier = 1.1;
5159 
5160  layer = "bottom";
5161 
5162  linestyleorder = "-";
5163  linestyleorderindex = 1.0;
5164 
5165  linewidth = 0.5;
5166 
5167  minorgridalpha = 0.25;
5168  minorgridalphamode = "auto";
5169  minorgridcolor = color_values (0.1, 0.1, 0.1);
5170  minorgridcolormode = "auto";
5171  minorgridlinestyle = ":";
5172 
5173  nextplot = "replace";
5174 
5175  // Note: plotboxaspectratio will be set through update_aspectratios
5176  plotboxaspectratiomode = "auto";
5177  projection = "orthographic";
5178 
5179  sortmethod = "depth";
5180 
5181  tickdir = "in";
5182  tickdirmode = "auto";
5183  ticklabelinterpreter = "tex";
5184  ticklength = default_axes_ticklength ();
5185 
5186  tightinset = Matrix (1, 4, 0.0);
5187 
5188  titlefontsizemultiplier = 1.1;
5189  titlefontweight = "bold";
5190 
5191  Matrix tview (1, 2, 0.0);
5192  tview(1) = 90;
5193  view = tview;
5194 
5195  xaxislocation = "bottom";
5196 
5197  xcolor = color_values (0.15, 0.15, 0.15);
5198  xcolormode = "auto";
5199  xdir = "normal";
5200  xgrid = "off";
5201  xminorgrid = "off";
5202  xminortick = "off";
5203  xscale = "linear";
5204  xtick = Matrix ();
5205  xticklabel = "";
5206  xticklabelmode = "auto";
5207  xticklabelrotation = 0.0;
5208  xtickmode = "auto";
5209 
5210  yaxislocation = "left";
5211 
5212  ycolor = color_values (0.15, 0.15, 0.15);
5213  ycolormode = "auto";
5214  ydir = "normal";
5215  ygrid = "off";
5216  yminorgrid = "off";
5217  yminortick = "off";
5218  yscale = "linear";
5219  ytick = Matrix ();
5220  yticklabel = "";
5221  yticklabelmode = "auto";
5222  yticklabelrotation = 0.0;
5223  ytickmode = "auto";
5224 
5225  zcolor = color_values (0.15, 0.15, 0.15);
5226  zcolormode = "auto";
5227  zdir = "normal";
5228  zgrid = "off";
5229  zminorgrid = "off";
5230  zminortick = "off";
5231  zscale = "linear";
5232  ztick = Matrix ();
5233  zticklabel = "";
5234  zticklabelmode = "auto";
5235  zticklabelrotation = 0.0;
5236  ztickmode = "auto";
5237 
5238  sx = "linear";
5239  sy = "linear";
5240  sz = "linear";
5241 
5242  visible = "on";
5243 
5244  graphics_object go = gh_manager::get_object (xlabel.handle_value ());
5246  go = gh_manager::get_object (ylabel.handle_value ());
5248  go = gh_manager::get_object (zlabel.handle_value ());
5250  go = gh_manager::get_object (title.handle_value ());
5252 
5253  xset (xlabel.handle_value (), "handlevisibility", "off");
5254  xset (ylabel.handle_value (), "handlevisibility", "off");
5255  xset (zlabel.handle_value (), "handlevisibility", "off");
5256  xset (title.handle_value (), "handlevisibility", "off");
5257 
5258  xset (xlabel.handle_value (), "horizontalalignment", "center");
5259  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
5260  xset (ylabel.handle_value (), "horizontalalignment", "center");
5261  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
5262  xset (zlabel.handle_value (), "horizontalalignment", "right");
5263  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
5264  xset (title.handle_value (), "horizontalalignment", "center");
5265  xset (title.handle_value (), "horizontalalignmentmode", "auto");
5266 
5267  xset (xlabel.handle_value (), "verticalalignment", "top");
5268  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
5269  xset (ylabel.handle_value (), "verticalalignment", "bottom");
5270  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
5271  xset (title.handle_value (), "verticalalignment", "bottom");
5272  xset (title.handle_value (), "verticalalignmentmode", "auto");
5273 
5274  xset (ylabel.handle_value (), "rotation", 90.0);
5275  xset (ylabel.handle_value (), "rotationmode", "auto");
5276 
5277  xset (zlabel.handle_value (), "visible", "off");
5278 
5279  xset (xlabel.handle_value (), "clipping", "off");
5280  xset (ylabel.handle_value (), "clipping", "off");
5281  xset (zlabel.handle_value (), "clipping", "off");
5282  xset (title.handle_value (), "clipping", "off");
5283 
5284  xset (xlabel.handle_value (), "__autopos_tag__", "xlabel");
5285  xset (ylabel.handle_value (), "__autopos_tag__", "ylabel");
5286  xset (zlabel.handle_value (), "__autopos_tag__", "zlabel");
5287  xset (title.handle_value (), "__autopos_tag__", "title");
5288 
5289  double fs;
5290  fs = labelfontsizemultiplier.double_value () * fontsize.double_value ();
5291  xset (xlabel.handle_value (), "fontsize", octave_value (fs));
5292  xset (ylabel.handle_value (), "fontsize", octave_value (fs));
5293  xset (zlabel.handle_value (), "fontsize", octave_value (fs));
5294  fs = titlefontsizemultiplier.double_value () * fontsize.double_value ();
5295  xset (title.handle_value (), "fontsize", octave_value (fs));
5296  xset (title.handle_value (), "fontweight", titlefontweight.get ());
5297 
5298  update_transform ();
5299  sync_positions ();
5300  override_defaults (bgo);
5301 }
5302 
5305 {
5306  if (__colormap__.get ().isempty ())
5307  {
5308  graphics_object go (gh_manager::get_object (get___myhandle__ ()));
5309  graphics_object go_f (go.get_ancestor ("figure"));
5310  figure::properties& figure_props
5311  = reinterpret_cast<figure::properties&> (go_f.get_properties ());
5312  return figure_props.get_colormap ();
5313  }
5314 
5315  return get___colormap__ ();
5316 }
5317 
5318 void
5320 {
5321  graphics_handle h = hp.handle_value ();
5322 
5323  if (h.ok ())
5324  {
5326 
5327  if (go.valid_object ())
5328  gh_manager::free (h);
5329 
5331  }
5332 
5333  // FIXME: is it necessary to check whether the axes object is
5334  // being deleted now? I think this function is only called when an
5335  // individual child object is delete and not when the parent axes
5336  // object is deleted.
5337 
5338  if (! is_beingdeleted ())
5339  {
5340  hp = gh_manager::make_graphics_handle ("text", __myhandle__,
5341  false, false);
5342 
5343  xset (hp.handle_value (), "handlevisibility", "off");
5344 
5345  adopt (hp.handle_value ());
5346  }
5347 }
5348 
5349 void
5351 {
5352  if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
5353  {
5354  delete_text_child (xlabel);
5355  update_xlabel_position ();
5356  }
5357  else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
5358  {
5359  delete_text_child (ylabel);
5360  update_ylabel_position ();
5361  }
5362  else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
5363  {
5364  delete_text_child (zlabel);
5365  update_zlabel_position ();
5366  }
5367  else if (title.handle_value ().ok () && h == title.handle_value ())
5368  {
5369  delete_text_child (title);
5370  update_title_position ();
5371  }
5372  else
5374 }
5375 
5376 inline Matrix
5378 {
5379  Matrix m (4, 4, 0.0);
5380 
5381  for (int i = 0; i < 4; i++)
5382  m(i,i) = 1;
5383 
5384  return m;
5385 }
5386 
5387 inline ColumnVector
5389 {
5390  ColumnVector v (4, 0.0);
5391 
5392  v(3) = 1;
5393 
5394  return v;
5395 }
5396 
5397 inline ColumnVector
5398 xform_vector (double x, double y, double z)
5399 {
5400  ColumnVector v (4, 1.0);
5401 
5402  v(0) = x;
5403  v(1) = y;
5404  v(2) = z;
5405 
5406  return v;
5407 }
5408 
5409 inline ColumnVector
5410 transform (const Matrix& m, double x, double y, double z)
5411 {
5412  return (m * xform_vector (x, y, z));
5413 }
5414 
5415 inline Matrix
5416 xform_scale (double x, double y, double z)
5417 {
5418  Matrix m (4, 4, 0.0);
5419 
5420  m(0,0) = x;
5421  m(1,1) = y;
5422  m(2,2) = z;
5423  m(3,3) = 1;
5424 
5425  return m;
5426 }
5427 
5428 inline Matrix
5429 xform_translate (double x, double y, double z)
5430 {
5431  Matrix m = xform_matrix ();
5432 
5433  m(0,3) = x;
5434  m(1,3) = y;
5435  m(2,3) = z;
5436  m(3,3) = 1;
5437 
5438  return m;
5439 }
5440 
5441 inline void
5442 scale (Matrix& m, double x, double y, double z)
5443 {
5444  m = m * xform_scale (x, y, z);
5445 }
5446 
5447 inline void
5448 translate (Matrix& m, double x, double y, double z)
5449 {
5450  m = m * xform_translate (x, y, z);
5451 }
5452 
5453 inline void
5454 xform (ColumnVector& v, const Matrix& m)
5455 {
5456  v = m * v;
5457 }
5458 
5459 inline void
5460 scale (ColumnVector& v, double x, double y, double z)
5461 {
5462  v(0) *= x;
5463  v(1) *= y;
5464  v(2) *= z;
5465 }
5466 
5467 inline void
5468 translate (ColumnVector& v, double x, double y, double z)
5469 {
5470  v(0) += x;
5471  v(1) += y;
5472  v(2) += z;
5473 }
5474 
5475 inline void
5477 {
5478  double fact = 1.0 / sqrt (v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
5479  scale (v, fact, fact, fact);
5480 }
5481 
5482 inline double
5483 dot (const ColumnVector& v1, const ColumnVector& v2)
5484 {
5485  return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
5486 }
5487 
5488 inline double
5489 norm (const ColumnVector& v)
5490 {
5491  return sqrt (dot (v, v));
5492 }
5493 
5494 inline ColumnVector
5495 cross (const ColumnVector& v1, const ColumnVector& v2)
5496 {
5497  ColumnVector r = xform_vector ();
5498 
5499  r(0) = v1(1)*v2(2) - v1(2)*v2(1);
5500  r(1) = v1(2)*v2(0) - v1(0)*v2(2);
5501  r(2) = v1(0)*v2(1) - v1(1)*v2(0);
5502 
5503  return r;
5504 }
5505 
5506 inline Matrix
5508 {
5509  static double data[32] =
5510  {
5511  0,0,0,1,
5512  1,0,0,1,
5513  0,1,0,1,
5514  0,0,1,1,
5515  1,1,0,1,
5516  1,0,1,1,
5517  0,1,1,1,
5518  1,1,1,1
5519  };
5520  Matrix m (4, 8);
5521 
5522  memcpy (m.fortran_vec (), data, sizeof (double)*32);
5523 
5524  return m;
5525 }
5526 
5527 inline ColumnVector
5529 {
5530  ColumnVector retval (4, 1.0);
5531 
5532  memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof (double)*3);
5533 
5534  return retval;
5535 }
5536 
5537 inline RowVector
5539 {
5540  return v.extract_n (0, 3).transpose ();
5541 }
5542 
5543 void
5545 {
5546  double xd = (xdir_is ("normal") ? 1 : -1);
5547  double yd = (ydir_is ("normal") ? 1 : -1);
5548  double zd = (zdir_is ("normal") ? 1 : -1);
5549 
5550  Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
5551  Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
5552  Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
5553 
5554  double xo = xlimits(xd > 0 ? 0 : 1);
5555  double yo = ylimits(yd > 0 ? 0 : 1);
5556  double zo = zlimits(zd > 0 ? 0 : 1);
5557 
5558  Matrix pb = get_plotboxaspectratio ().matrix_value ();
5559 
5560  bool autocam = (camerapositionmode_is ("auto")
5561  && cameratargetmode_is ("auto")
5562  && cameraupvectormode_is ("auto")
5563  && cameraviewanglemode_is ("auto"));
5564  bool dowarp = (autocam && dataaspectratiomode_is ("auto")
5565  && plotboxaspectratiomode_is ("auto"));
5566 
5567  ColumnVector c_eye (xform_vector ());