GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
__init_fltk__.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2007-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 /*
27 
28 To initialize:
29 
30  graphics_toolkit ("fltk");
31  plot (randn (1e3, 1));
32 
33 */
34 
35 // PKG_ADD: if (__have_feature__ ("FLTK") && __have_feature__ ("OPENGL") && have_window_system () && ! (ismac () && __event_manager_enabled__ ())) register_graphics_toolkit ("fltk"); endif
36 
37 #if defined (HAVE_CONFIG_H)
38 # include "config.h"
39 #endif
40 
41 #include "defun-dld.h"
42 #include "error.h"
43 #include "errwarn.h"
44 
45 #if defined (HAVE_FLTK)
46 
47 #if defined (HAVE_X_WINDOWS)
48 # include <X11/Xlib.h>
49 #endif
50 
51 #include <cmath>
52 
53 #include <locale>
54 #include <map>
55 #include <sstream>
56 #include <string>
57 #include <vector>
58 
59 #if defined (WIN32)
60 # define WIN32_LEAN_AND_MEAN
61 #endif
62 
63 #include <FL/Fl.H>
64 #include <FL/Fl_Box.H>
65 #include <FL/Fl_Button.H>
66 #include <FL/Fl_Choice.H>
67 #include <FL/Fl_File_Chooser.H>
68 #include <FL/Fl_Gl_Window.H>
69 #include <FL/names.h>
70 #include <FL/Fl_Menu_Bar.H>
71 #include <FL/Fl_Menu_Button.H>
72 #include <FL/Fl_Output.H>
73 #include <FL/Fl_Window.H>
74 #include <FL/fl_ask.H>
75 #include <FL/fl_draw.H>
76 #include <FL/gl.h>
77 
78 // FLTK headers may include X11/X.h which defines Complex, and that
79 // conflicts with Octave's Complex typedef. We don't need the X11
80 // Complex definition in this file, so remove it before including Octave
81 // headers which may require Octave's Complex typedef.
82 #undef Complex
83 
84 #include "Array.h"
85 #include "cmd-edit.h"
86 #include "dColVector.h"
87 #include "dMatrix.h"
88 #include "lo-ieee.h"
89 #include "oct-env.h"
90 
91 #include "Cell.h"
92 #include "builtin-defun-decls.h"
93 #include "display.h"
94 #include "gl-render.h"
95 #include "gl2ps-print.h"
96 #include "graphics.h"
97 #include "gtk-manager.h"
98 #include "interpreter-private.h"
99 #include "interpreter.h"
100 #include "oct-map.h"
101 #include "oct-opengl.h"
102 #include "ov-fcn-handle.h"
103 #include "ov.h"
104 #include "ovl.h"
105 #include "parse.h"
106 #include "variables.h"
107 
108 #define FLTK_GRAPHICS_TOOLKIT_NAME "fltk"
109 
110 const char *help_text = "\
111 Keyboard Shortcuts\n\
112 a - autoscale\n\
113 p - pan/zoom\n\
114 r - rotate\n\
115 g - toggle grid\n\
116 \n\
117 Mouse\n\
118 left drag - pan\n\
119 mouse wheel - zoom\n\
120 right drag - rectangle zoom\n\
121 left double click - autoscale\n\
122 ";
123 
124 class OpenGL_fltk : public Fl_Gl_Window
125 {
126 public:
127 
128  OpenGL_fltk (int xx, int yy, int ww, int hh, double num)
129  : Fl_Gl_Window (xx, yy, ww, hh, nullptr), m_number (num),
130  m_glfcns (), m_renderer (m_glfcns), m_in_zoom (false), m_zoom_box ()
131  {
132 #if defined (HAVE_OPENGL)
133 
134  // Ask for double buffering and a depth buffer.
135  mode (FL_DEPTH | FL_DOUBLE | FL_MULTISAMPLE);
136 
137 #else
138 
139  err_disabled_feature ("OpenGL_fltk", "OpenGL");
140 
141 #endif
142  }
143 
144  ~OpenGL_fltk (void) = default;
145 
146  void zoom (bool z)
147  {
148  m_in_zoom = z;
149  if (! m_in_zoom)
150  hide_overlay ();
151  }
152 
153  bool zoom (void) { return m_in_zoom; }
154  void set_zoom_box (const Matrix& zb) { m_zoom_box = zb; }
155 
156  void print (const std::string& cmd, const std::string& term)
157  {
158  gh_manager& gh_mgr = octave::__get_gh_manager__ ("OpenGL_fltk::print");
159 
160  octave::gl2ps_print (m_glfcns, gh_mgr.get_object (m_number), cmd, term);
161  }
162 
163  uint8NDArray get_pixels (void)
164  {
165  gh_manager& gh_mgr = octave::__get_gh_manager__ ("OpenGL_fltk::get_pixels");
166 
167  m_renderer.draw (gh_mgr.get_object (m_number));
168 
169  return m_renderer.get_pixels (w (), h ());
170  }
171 
172  void resize (int xx, int yy, int ww, int hh)
173  {
174 #if defined (HAVE_OPENGL)
175 
176  Fl_Gl_Window::resize (xx, yy, ww, hh);
177 
178 #else
179 
180  octave_unused_parameter (xx);
181  octave_unused_parameter (yy);
182  octave_unused_parameter (ww);
183  octave_unused_parameter (hh);
184 
185  // This shouldn't happen because construction of Opengl_fltk
186  // objects is supposed to be impossible if OpenGL is not available.
187 
188  panic_impossible ();
189 
190 #endif
191  }
192 
193  bool renumber (double new_number)
194  {
195  bool retval = false;
196 
197  if (m_number != new_number)
198  {
199  m_number = new_number;
200  retval = true;
201  }
202 
203  return retval;
204  }
205 
206 private:
207 
208  double m_number;
209 
210  octave::opengl_functions m_glfcns;
211  octave::opengl_renderer m_renderer;
212 
213  bool m_in_zoom;
214 
215  // (x1,y1,x2,y2)
216  Matrix m_zoom_box;
217 
218  void draw (void)
219  {
220 #if defined (HAVE_OPENGL)
221 
222  if (! valid ())
223  {
224  m_glfcns.glMatrixMode (GL_PROJECTION);
225  m_glfcns.glLoadIdentity ();
226  m_glfcns.glViewport (0, 0, w (), h ());
227  }
228 
229  gh_manager& gh_mgr = octave::__get_gh_manager__ ("OpenGL_fltk::draw");
230 
231  m_renderer.draw (gh_mgr.get_object (m_number));
232 
233  if (zoom ())
234  overlay ();
235 
236 #else
237 
238  // This shouldn't happen because construction of Opengl_fltk
239  // objects is supposed to be impossible if OpenGL is not available.
240 
241  panic_impossible ();
242 
243 #endif
244  }
245 
246  void overlay (void)
247  {
248  Matrix overlaycolor (3, 1);
249  overlaycolor(0) = 0.45;
250  overlaycolor(1) = 0.62;
251  overlaycolor(2) = 0.81;
252  double overlayalpha = 0.1;
253  Matrix bordercolor = overlaycolor;
254  double borderalpha = 0.9;
255  double borderwidth = 1.5;
256 
257  m_renderer.draw_zoom_box (w (), h (),
258  m_zoom_box(0), m_zoom_box(1),
259  m_zoom_box(2), m_zoom_box(3),
260  overlaycolor, overlayalpha,
261  bordercolor, borderalpha, borderwidth);
262  }
263 
264  int handle (int event)
265  {
266 #if defined (HAVE_OPENGL)
267 
268  switch (event)
269  {
270  case FL_ENTER:
271  cursor (FL_CURSOR_CROSS);
272  return 1;
273 
274  case FL_LEAVE:
275  cursor (FL_CURSOR_DEFAULT);
276  return 1;
277  }
278 
279  return Fl_Gl_Window::handle (event);
280 
281 #else
282 
283  octave_unused_parameter (event);
284 
285  // This shouldn't happen because construction of Opengl_fltk
286  // objects is supposed to be impossible if OpenGL is not available.
287 
288  panic_impossible ();
289 
290 #endif
291  }
292 };
293 
294 void script_cb (Fl_Widget *, void *data)
295 {
296  static_cast<uimenu::properties *> (data)->execute_callback ();
297 }
298 
299 class fltk_uimenu
300 {
301 public:
302 
303  fltk_uimenu (int xx, int yy, int ww, int hh)
304  : m_menubar (new Fl_Menu_Bar (xx, yy, ww, hh))
305  { }
306 
307  int items_to_show (void)
308  {
309  //returns the number of visible menu items
310  int len = m_menubar->size ();
311  int n = 0;
312  for (int t = 0; t < len; t++)
313  {
314  const Fl_Menu_Item *m
315  = static_cast<const Fl_Menu_Item *> (&(m_menubar->menu ()[t]));
316 
317  if (m->label () && m->visible ())
318  n++;
319  }
320 
321  return n;
322  }
323 
324  void show (void)
325  {
326  m_menubar->show ();
327  m_menubar->redraw ();
328  }
329 
330  void hide (void)
331  {
332  m_menubar->hide ();
333  m_menubar->redraw ();
334  }
335 
336  bool is_visible (void)
337  {
338  return m_menubar->visible ();
339  }
340 
341  int find_index_by_name (const std::string& findname)
342  {
343  // This function is derived from Greg Ercolano's function
344  // int GetIndexByName(...), see:
345  // http://seriss.com/people/erco/fltk/#Menu_ChangeLabel
346  // He agreed via PM that it can be included in octave using GPLv3
347  // Kai Habel (14.10.2010)
348 
349  std::string menupath;
350  for (int t = 0; t < m_menubar->size (); t++)
351  {
352  Fl_Menu_Item *m = const_cast<Fl_Menu_Item *> (&(m_menubar->menu ()[t]));
353  if (m->submenu ())
354  {
355  // item has submenu
356  if (! menupath.empty ())
357  menupath += '/';
358  menupath += m->label ();
359 
360  if (menupath == findname)
361  return (t);
362  }
363  else
364  {
365  // End of submenu? Pop back one level.
366  if (! m->label ())
367  {
368  size_t idx = menupath.find_last_of ('/');
369  if (idx != std::string::npos)
370  menupath.erase (idx);
371  else
372  menupath.clear ();
373  continue;
374  }
375  // Menu item?
376  std::string itempath = menupath;
377  if (! itempath.empty ())
378  itempath += '/';
379  itempath += m->label ();
380 
381  if (itempath == findname)
382  return (t);
383  }
384  }
385  return (-1);
386  }
387 
388  Matrix find_uimenu_children (uimenu::properties& uimenup) const
389  {
390  Matrix uimenu_childs = uimenup.get_all_children ();
391  Matrix retval = do_find_uimenu_children (uimenu_childs);
392  return retval;
393  }
394 
395  Matrix find_uimenu_children (figure::properties& figp) const
396  {
397  Matrix uimenu_childs = figp.get_all_children ();
398  Matrix retval = do_find_uimenu_children (uimenu_childs);
399  return retval;
400  }
401 
402  Matrix do_find_uimenu_children (Matrix uimenu_childs) const
403  {
404  octave_idx_type k = 0;
405 
406  Matrix pos = Matrix (uimenu_childs.numel (), 1);
407 
408  for (octave_idx_type ii = 0; ii < uimenu_childs.numel (); ii++)
409  {
410  gh_manager& gh_mgr
411  = octave::__get_gh_manager__ ("fltk_uimenu::do_find_uimenu_children");
412 
413  graphics_object kidgo = gh_mgr.get_object (uimenu_childs (ii));
414 
415  if (kidgo.valid_object () && kidgo.isa ("uimenu"))
416  {
417  uimenu_childs(k) = uimenu_childs(ii);
418  pos(k++) = dynamic_cast<uimenu::properties&> (kidgo.get_properties ()).get_position ();
419  }
420  }
421 
422  uimenu_childs.resize (k, 1);
423  pos.resize (k, 1);
424  Matrix retval = Matrix (k, 1);
425  // Don't know if this is the best method to sort.
426  // Can we avoid the for loop?
428  for (octave_idx_type ii = 0; ii < k; ii++)
429  retval(ii) = uimenu_childs (sidx(ii));
430 
431  return retval;
432  }
433 
434  void delete_entry (uimenu::properties& uimenup)
435  {
436  std::string fltk_label = uimenup.get___fltk_label__ ();
437  int idx = find_index_by_name (fltk_label.c_str ());
438 
439  if (idx >= 0)
440  m_menubar->remove (idx);
441  }
442 
443  void update_accelerator (uimenu::properties& uimenup)
444  {
445  std::string fltk_label = uimenup.get___fltk_label__ ();
446  if (! fltk_label.empty ())
447  {
448  Fl_Menu_Item *item = const_cast<Fl_Menu_Item *> (m_menubar->find_item (fltk_label.c_str ()));
449 
450  if (item)
451  {
452  std::string acc = uimenup.get_accelerator ();
453  if (acc.length () > 0)
454  {
455  int key = FL_CTRL + acc[0];
456  item->shortcut (key);
457  }
458  }
459  }
460  }
461 
462  void update_callback (uimenu::properties& uimenup)
463  {
464  std::string fltk_label = uimenup.get___fltk_label__ ();
465  if (! fltk_label.empty ())
466  {
467  Fl_Menu_Item *item
468  = const_cast<Fl_Menu_Item *> (m_menubar->find_item (fltk_label.c_str ()));
469  if (item)
470  {
471  if (! uimenup.get_callback ().isempty ())
472  item->callback (static_cast<Fl_Callback *> (script_cb),
473  static_cast<void *> (&uimenup));
474  else
475  item->callback (nullptr, static_cast<void *> (nullptr));
476  }
477  }
478  }
479 
480  void update_enable (uimenu::properties& uimenup)
481  {
482  std::string fltk_label = uimenup.get___fltk_label__ ();
483  if (! fltk_label.empty ())
484  {
485  Fl_Menu_Item *item
486  = const_cast<Fl_Menu_Item *> (m_menubar->find_item (fltk_label.c_str ()));
487  if (item)
488  {
489  if (uimenup.is_enable ())
490  item->activate ();
491  else
492  item->deactivate ();
493  }
494  }
495  }
496 
497  void update_foregroundcolor (uimenu::properties& uimenup)
498  {
499  std::string fltk_label = uimenup.get___fltk_label__ ();
500  if (! fltk_label.empty ())
501  {
502  Fl_Menu_Item *item
503  = const_cast<Fl_Menu_Item *> (m_menubar->find_item (fltk_label.c_str ()));
504  if (item)
505  {
506  Matrix rgb = uimenup.get_foregroundcolor_rgb ();
507 
508  uchar r = static_cast<uchar> (std::floor (rgb (0) * 255));
509  uchar g = static_cast<uchar> (std::floor (rgb (1) * 255));
510  uchar b = static_cast<uchar> (std::floor (rgb (2) * 255));
511 
512  item->labelcolor (fl_rgb_color (r, g, b));
513  }
514  }
515  }
516 
517  void update_seperator (const uimenu::properties& uimenup)
518  {
519  // Matlab places the separator before the current
520  // menu entry, while fltk places it after. So we need to find
521  // the previous item in this menu/submenu. (Kai)
522  std::string fltk_label = uimenup.get___fltk_label__ ();
523  if (! fltk_label.empty ())
524  {
525  int itemflags = 0, idx;
526  int curr_idx = find_index_by_name (fltk_label.c_str ());
527 
528  for (idx = curr_idx - 1; idx >= 0; idx--)
529  {
530  Fl_Menu_Item *item
531  = const_cast<Fl_Menu_Item *> (&m_menubar->menu () [idx]);
532  itemflags = item->flags;
533  if (item->label ())
534  break;
535  }
536 
537  if (idx >= 0 && idx < m_menubar->size ())
538  {
539  if (uimenup.is_separator ())
540  {
541  if (! (itemflags & FL_SUBMENU))
542  m_menubar->mode (idx, itemflags | FL_MENU_DIVIDER);
543  }
544  else
545  m_menubar->mode (idx, itemflags & (~FL_MENU_DIVIDER));
546  }
547  }
548  }
549 
550  void update_visible (uimenu::properties& uimenup)
551  {
552  std::string fltk_label = uimenup.get___fltk_label__ ();
553  if (! fltk_label.empty ())
554  {
555  Fl_Menu_Item *item
556  = const_cast<Fl_Menu_Item *> (m_menubar->find_item (fltk_label.c_str ()));
557  if (item)
558  {
559  if (uimenup.is_visible ())
560  item->show ();
561  else
562  item->hide ();
563  }
564  }
565  }
566 
567  void update_position (uimenu::properties& uimenup, int pos)
568  {
569  uimenup.get_property ("position").set (octave_value (static_cast<double> (pos)),
570  true, false);
571  }
572 
573  void add_entry (uimenu::properties& uimenup)
574  {
575 
576  std::string fltk_label = uimenup.get___fltk_label__ ();
577 
578  if (! fltk_label.empty ())
579  {
580  bool item_added = false;
581  do
582  {
583  const Fl_Menu_Item *item
584  = m_menubar->find_item (fltk_label.c_str ());
585 
586  if (item)
587  {
588  //avoid duplicate menulabels
589  size_t idx1 = fltk_label.find_last_of ('(');
590  size_t idx2 = fltk_label.find_last_of (')');
591  int len = idx2 - idx1;
592  int val = 1;
593  if (len > 0)
594  {
595  std::string valstr = fltk_label.substr (idx1 + 1, len - 1);
596  fltk_label.erase (idx1, len + 1);
597  val = atoi (valstr.c_str ());
598  if (val > 0 && val < 99)
599  val++;
600  }
601  std::ostringstream valstream;
602  valstream << val;
603  fltk_label += '(' + valstream.str () + ')';
604  }
605  else
606  {
607  Matrix uimenu_ch = find_uimenu_children (uimenup);
608  int len = uimenu_ch.numel ();
609  int flags = 0;
610  if (len > 0)
611  flags = FL_SUBMENU;
612  if (len == 0 && uimenup.is_checked ())
613  flags += FL_MENU_TOGGLE + FL_MENU_VALUE;
614  m_menubar->add (fltk_label.c_str (),
615  0, nullptr, nullptr, flags);
616  item_added = true;
617  }
618  }
619  while (! item_added);
620  uimenup.set___fltk_label__ (fltk_label);
621  }
622  }
623 
624  void add_to_menu (uimenu::properties& uimenup)
625  {
626  std::vector<int> delayed_menus;
627  Matrix kids = find_uimenu_children (uimenup);
628  int len = kids.numel ();
629  std::string fltk_label = uimenup.get___fltk_label__ ();
630  int count = 0;
631 
632  add_entry (uimenup);
633  update_foregroundcolor (uimenup);
634  update_callback (uimenup);
635  update_accelerator (uimenup);
636  update_enable (uimenup);
637  update_visible (uimenup);
638  update_seperator (uimenup);
639 
640  gh_manager& gh_mgr = octave::__get_gh_manager__ ("fltk_uimenu::add_to_menu");
641 
642  for (octave_idx_type ii = 0; ii < len; ii++)
643  {
644  graphics_object kgo = gh_mgr.get_object (kids (len - (ii + 1)));
645 
646  if (kgo.valid_object ())
647  {
648  uimenu::properties& kprop = dynamic_cast<uimenu::properties&>
649  (kgo.get_properties ());
650 
651  // if no pos yet, delay adding menu until after other menus
652  int pos = kprop.get_position ();
653  if (pos <= 0)
654  delayed_menus.push_back ((len - (ii + 1)));
655  else
656  {
657  add_to_menu (kprop);
658  }
659  }
660  }
661 
662  // create any delayed menus
663  for (size_t ii = 0; ii < delayed_menus.size (); ii++)
664  {
665  graphics_object kgo = gh_mgr.get_object (kids (delayed_menus[ii]));
666 
667  if (kgo.valid_object ())
668  {
669  uimenu::properties& kprop = dynamic_cast<uimenu::properties&>
670  (kgo.get_properties ());
671  add_to_menu (kprop);
672  update_position (kprop, ++count);
673  }
674  }
675  }
676 
677  void add_to_menu (figure::properties& figp)
678  {
679  std::vector<int> delayed_menus;
680  Matrix kids = find_uimenu_children (figp);
681  int len = kids.numel ();
682  int count = 0;
683 
684  m_menubar->clear ();
685 
686  gh_manager& gh_mgr = octave::__get_gh_manager__ ("fltk_uimenu::add_to_menu");
687 
688  for (octave_idx_type ii = 0; ii < len; ii++)
689  {
690  graphics_object kgo = gh_mgr.get_object (kids (len - (ii + 1)));
691 
692  if (kgo.valid_object ())
693  {
694  uimenu::properties& kprop = dynamic_cast<uimenu::properties&>
695  (kgo.get_properties ());
696 
697  // if no pos yet, delay adding menu until after other menus
698  int pos = kprop.get_position ();
699  if (pos <= 0)
700  delayed_menus.push_back ((len - (ii + 1)));
701  else
702  {
703  add_to_menu (kprop);
704  update_position (kprop, ++count);
705  }
706  }
707  }
708 
709  // create any delayed menus
710  for (size_t ii = 0; ii < delayed_menus.size (); ii++)
711  {
712  graphics_object kgo = gh_mgr.get_object (kids (delayed_menus[ii]));
713 
714  if (kgo.valid_object ())
715  {
716  uimenu::properties& kprop = dynamic_cast<uimenu::properties&>
717  (kgo.get_properties ());
718  add_to_menu (kprop);
719  update_position (kprop, ++count);
720  }
721  }
722  }
723 
724  template <typename T_prop>
725  void remove_from_menu (T_prop& prop)
726  {
727  Matrix kids;
728  std::string type = prop.get_type ();
729  kids = find_uimenu_children (prop);
730  int len = kids.numel ();
731 
732  gh_manager& gh_mgr = octave::__get_gh_manager__ ("fltk_uimenu::remove_from_menu");
733 
734  for (octave_idx_type ii = 0; ii < len; ii++)
735  {
736  graphics_object kgo = gh_mgr.get_object (kids (len - (ii + 1)));
737 
738  if (kgo.valid_object ())
739  {
740  uimenu::properties kprop = dynamic_cast<uimenu::properties&>
741  (kgo.get_properties ());
742  remove_from_menu (kprop);
743  }
744  }
745 
746  if (type == "uimenu")
747  delete_entry (dynamic_cast<uimenu::properties&> (prop));
748  else if (type == "figure")
749  m_menubar->clear ();
750  }
751 
752  // No copying!
753 
754  fltk_uimenu (const fltk_uimenu&) = delete;
755 
756  fltk_uimenu operator = (const fltk_uimenu&) = delete;
757 
758  ~fltk_uimenu (void)
759  {
760  // FLTK is supposed to manage memory for widgets.
761  }
762 
763 private:
764 
765  Fl_Menu_Bar *m_menubar;
766 };
767 
768 #if defined (HAVE_X_WINDOWS)
769 static int
770 xerror_handler (Display *, XErrorEvent *)
771 {
772  return 0;
773 }
774 #endif
775 
776 class plot_window : public Fl_Window
777 {
778  friend class fltk_uimenu;
779 
780 public:
781 
782  plot_window (int xx, int yy, int ww, int hh, figure::properties& xfp,
783  bool internal)
784  : Fl_Window (xx, yy, ww, hh + m_menu_h + m_status_h + 2, "octave"),
785  m_window_label (), m_fp (xfp), m_uimenu (nullptr), m_canvas (nullptr),
786  m_autoscale (nullptr), m_togglegrid (nullptr), m_panzoom (nullptr),
787  m_rotate (nullptr), m_help (nullptr), m_status (nullptr),
788  m_resize_dummy (nullptr), m_ax_obj (), m_pos_x (0), m_pos_y (0)
789  {
790  callback (window_close, static_cast<void *> (this));
791 
792  // The size of the resize_dummy box also determines the minimum
793  // window size.
794  m_resize_dummy = new Fl_Box (5 * m_status_h, m_menu_h,
795  ww - 5 * m_status_h, hh);
796 
797  // See http://fltk.org/articles.php?L415+I0+T+M1000+P1
798  // for how resizable works
799  resizable (m_resize_dummy);
800 
801  // FIXME: The function below is only available in FLTK >= 1.3
802  // At some point support for FLTK 1.1 will be dropped in Octave.
803  // At that point this function should be uncommented.
804  // The current solution is to call xclass() before show() for each window.
805  // Set WM_CLASS which allows window managers to properly group related
806  // windows. Otherwise, the class is just "FLTK"
807  //default_xclass ("Octave");
808 
809  m_uimenu = new fltk_uimenu (0, 0, ww, m_menu_h);
810  m_canvas = new OpenGL_fltk (0, m_menu_h, ww, hh, number ());
811 
812  // The bottom toolbar is a composite of "autoscale", "togglegrid",
813  // "panzoom", "rotate", "help", and "status".
814  // Only "status" should be resized.
815 
816  int toolbar_y = m_menu_h + hh + 1;
817  m_status = new Fl_Output (5 * m_status_h, toolbar_y,
818  ww - 5 * m_status_h, m_status_h, "");
819 
820  m_status->textcolor (FL_BLACK);
821  m_status->color (FL_GRAY);
822  m_status->textfont (FL_COURIER);
823  m_status->textsize (10);
824  m_status->box (FL_ENGRAVED_BOX);
825 
826  m_autoscale = new Fl_Button (0, toolbar_y, m_status_h, m_status_h, "A");
827  m_autoscale->callback (button_callback, static_cast<void *> (this));
828  m_autoscale->tooltip ("Autoscale");
829 
830  m_togglegrid = new Fl_Button (m_status_h, toolbar_y, m_status_h, m_status_h, "G");
831  m_togglegrid->callback (button_callback, static_cast<void *> (this));
832  m_togglegrid->tooltip ("Toggle Grid");
833 
834  m_panzoom = new Fl_Button (2* m_status_h, toolbar_y, m_status_h, m_status_h, "P");
835  m_panzoom->callback (button_callback, static_cast<void *> (this));
836  m_panzoom->tooltip ("Mouse Pan/Zoom");
837 
838  m_rotate = new Fl_Button (3 * m_status_h, toolbar_y, m_status_h, m_status_h, "R");
839  m_rotate->callback (button_callback, static_cast<void *> (this));
840  m_rotate->tooltip ("Mouse Rotate");
841 
842  m_help = new Fl_Button (4 * m_status_h, toolbar_y, m_status_h, m_status_h, "?");
843  m_help->callback (button_callback, static_cast<void *> (this));
844  m_help->tooltip ("Help");
845 
846  end ();
847 
848  set_name ();
849  m_uimenu->add_to_menu (m_fp);
850  if (m_fp.menubar_is ("none") || ! m_uimenu->items_to_show ())
851  hide_menubar ();
852 
853  update_boundingbox (internal);
854 
855  if (m_fp.is_visible ())
856  {
857  // FIXME: This code should be removed when Octave drops support
858  // for FLTK 1.1. Search for default_xclass in this file to find
859  // code that should be uncommented to take its place.
860  //
861  // Set WM_CLASS which allows window managers to properly group
862  // related windows. Otherwise, the class is just "FLTK"
863  xclass ("Octave");
864 
865  show ();
866 
867 #if defined (HAVE_X_WINDOWS)
868  std::string show_gui_msgs
869  = octave::sys::env::getenv ("OCTAVE_SHOW_GUI_MESSAGES");
870 
871  // Installing our handler suppresses the messages.
872  if (show_gui_msgs.empty ())
873  XSetErrorHandler (xerror_handler);
874 #endif
875 
876  if (m_fp.get_currentaxes ().ok ())
877  show_canvas ();
878  else
879  hide_canvas ();
880  }
881  }
882 
883  // No copying!
884 
885  plot_window (const plot_window&) = delete;
886 
887  plot_window& operator = (const plot_window&) = delete;
888 
889  ~plot_window (void)
890  {
891  this->hide ();
892  Fl::check ();
893 
894  delete m_uimenu;
895 
896  // FLTK is supposed to manage memory for widgets.
897  }
898 
899  double number (void) { return m_fp.get___myhandle__ ().value (); }
900 
901  void renumber (double new_number)
902  {
903  if (! m_canvas)
904  error ("unable to renumber figure");
905 
906  if (m_canvas->renumber (new_number))
907  mark_modified ();
908  }
909 
910  void print (const std::string& cmd, const std::string& term)
911  {
912  m_canvas->print (cmd, term);
913  }
914 
915  uint8NDArray get_pixels ()
916  {
917  return m_canvas->get_pixels ();
918  }
919 
920  void show_menubar (void)
921  {
922  m_uimenu->show ();
923  update_toolbar_position ();
924  }
925 
926  void hide_menubar (void)
927  {
928  m_uimenu->hide ();
929  update_toolbar_position ();
930  }
931 
932  void uimenu_update (const graphics_handle& gh, int id)
933  {
934  gh_manager& gh_mgr = octave::__get_gh_manager__ ("plot_window::uimenu_update");
935 
936  graphics_object uimenu_obj = gh_mgr.get_object (gh);
937 
938  if (uimenu_obj.valid_object () && uimenu_obj.isa ("uimenu"))
939  {
940  uimenu::properties& uimenup
941  = dynamic_cast<uimenu::properties&> (uimenu_obj.get_properties ());
942  std::string fltk_label = uimenup.get___fltk_label__ ();
943  graphics_object fig = uimenu_obj.get_ancestor ("figure");
944  figure::properties& figp
945  = dynamic_cast<figure::properties&> (fig.get_properties ());
946 
947  switch (id)
948  {
949  case base_properties::ID_BEINGDELETED:
950  m_uimenu->remove_from_menu (uimenup);
951  break;
952 
953  case base_properties::ID_VISIBLE:
954  m_uimenu->update_visible (uimenup);
955  break;
956 
957  case uimenu::properties::ID_ACCELERATOR:
958  m_uimenu->update_accelerator (uimenup);
959  break;
960 
961  case uimenu::properties::ID_CALLBACK:
962  m_uimenu->update_callback (uimenup);
963  break;
964 
965  case uimenu::properties::ID_CHECKED:
966  m_uimenu->add_to_menu (figp);//rebuilding entire menu
967  break;
968 
969  case uimenu::properties::ID_ENABLE:
970  m_uimenu->update_enable (uimenup);
971  break;
972 
973  case uimenu::properties::ID_FOREGROUNDCOLOR:
974  m_uimenu->update_foregroundcolor (uimenup);
975  break;
976 
977  case uimenu::properties::ID_LABEL:
978  m_uimenu->add_to_menu (figp);//rebuilding entire menu
979  break;
980 
981  case uimenu::properties::ID_POSITION:
982  m_uimenu->add_to_menu (figp);//rebuilding entire menu
983  break;
984 
985  case uimenu::properties::ID_SEPARATOR:
986  m_uimenu->update_seperator (uimenup);
987  break;
988  }
989 
990  if (m_uimenu->items_to_show ())
991  show_menubar ();
992  else
993  hide_menubar ();
994  }
995  }
996 
997  void show_canvas (void)
998  {
999  if (! m_canvas->can_do ())
1000  error ("unable to plot due to insufficient OpenGL support");
1001  else if (m_fp.is_visible ())
1002  {
1003  m_canvas->show ();
1004  m_canvas->make_current ();
1005  }
1006  }
1007 
1008  void hide_canvas (void)
1009  {
1010  m_canvas->hide ();
1011  }
1012 
1013  // Move the toolbar at the bottom of the plot_window.
1014  // The only reason for moving the toolbar is hiding and
1015  // showing the menubar. All other resizing is done by fltk.
1016 
1017  void update_toolbar_position ()
1018  {
1019  int old_canvas_h = m_canvas->h ();
1020 
1021  // keep position fix, change outerposition accordingly
1022  update_boundingbox (true);
1023  m_canvas->resize (0, menu_dy (), w (), old_canvas_h);
1024 
1025  int toolbar_y = m_canvas->h () + menu_dy () + 1;
1026  m_autoscale->position (0, toolbar_y);
1027  m_togglegrid->position (m_status_h, toolbar_y);
1028  m_panzoom->position (2 * m_status_h, toolbar_y);
1029  m_rotate->position (3 * m_status_h, toolbar_y);
1030  m_help->position (4 * m_status_h, toolbar_y);
1031  m_status->resize (5 * m_status_h, toolbar_y,
1032  w () - 5 * m_status_h, m_status_h);
1033  init_sizes ();
1034  redraw ();
1035  }
1036 
1037  Matrix outerposition2position (const Matrix& outerpos)
1038  {
1039  Matrix pos = outerpos;
1040  pos(1) += menu_dy ();
1041  pos(3) -= menu_dy () + m_status_h + 2;
1042  return pos;
1043  }
1044 
1045  Matrix position2outerposition (const Matrix& pos)
1046  {
1047  Matrix outerpos = pos;
1048  outerpos(1) -= menu_dy ();
1049  outerpos(3) += menu_dy () + m_status_h + 2;
1050  return outerpos;
1051  }
1052 
1053  // Called from figure::properties::ID_POSITION if internal = true
1054  // or ID_OUTERPOSITION if false.
1055  // (someone has requested a position change with set (h, "position", [...])
1056  // or set (h, "outerposition", [...])
1057 
1058  void update_boundingbox (bool internal)
1059  {
1060  Matrix bb = m_fp.get_boundingbox (internal);
1061  if (internal)
1062  bb = position2outerposition (bb);
1063  resize (bb(0), bb(1), bb(2), bb(3));
1064  }
1065 
1066  void mark_modified (void)
1067  {
1068  m_canvas->redraw ();
1069  }
1070 
1071  void set_name (void)
1072  {
1073  m_window_label = m_fp.get_title ();
1074  label (m_window_label.c_str ());
1075  }
1076 
1077 private:
1078 
1079  // window name -- this must exists for the duration of the window's
1080  // life
1081  std::string m_window_label;
1082 
1083  // Figure properties.
1084  figure::properties& m_fp;
1085 
1086  // Status area height.
1087  static const int m_status_h = 20;
1088 
1089  // Menu height
1090  static const int m_menu_h = 25;
1091 
1092  fltk_uimenu *m_uimenu;
1093 
1094  OpenGL_fltk *m_canvas;
1095 
1096  Fl_Button *m_autoscale;
1097  Fl_Button *m_togglegrid;
1098  Fl_Button *m_panzoom;
1099  Fl_Button *m_rotate;
1100  Fl_Button *m_help;
1101  Fl_Output *m_status;
1102 
1103  Fl_Box *m_resize_dummy;
1104 
1105  graphics_object m_ax_obj;
1106 
1107  int m_pos_x;
1108  int m_pos_y;
1109 
1110  // Window callback.
1111  static void window_close (Fl_Widget *, void *data)
1112  {
1113  octave_value_list args;
1114  args(0) = static_cast<plot_window *> (data)->number ();
1115  octave::feval ("close", args);
1116  }
1117 
1118  // Button callbacks.
1119  static void button_callback (Fl_Widget *ww, void *data)
1120  {
1121  static_cast<plot_window *> (data)->button_press (ww, data);
1122  }
1123 
1124  void button_press (Fl_Widget *widg, void *)
1125  {
1126  if (widg == m_autoscale)
1127  axis_auto ();
1128  else if (widg == m_togglegrid)
1129  toggle_grid ();
1130  else if (widg == m_panzoom)
1131  m_fp.set___mouse_mode__ ("pan");
1132  else if (widg == m_rotate)
1133  m_fp.set___mouse_mode__ ("rotate");
1134  else if (widg == m_help)
1135  fl_message ("%s", help_text);
1136  }
1137 
1138  void set_on_ax_obj (const std::string& name, const std::string& value)
1139  {
1140  // ax_obj is the last clicked axes object
1141  if (m_ax_obj && m_ax_obj.isa ("axes")
1142  && m_ax_obj.get_properties ().get_tag () != "legend"
1143  && m_ax_obj.get_properties ().get_tag () != "colorbar")
1144  {
1145  axes::properties& ap
1146  = dynamic_cast<axes::properties&>(m_ax_obj.get_properties ());
1147  ap.set (name, value);
1148  }
1149  else // no axes object clicked so far, take currentaxes
1150  {
1151  graphics_handle gh = m_fp.get_currentaxes ();
1152  if (gh.ok ())
1153  {
1154  gh_manager& gh_mgr
1155  = octave::__get_gh_manager__ ("plot_window::set_on_ax_obj");
1156 
1157  graphics_object go = gh_mgr.get_object (gh);
1158 
1159  axes::properties& ap
1160  = dynamic_cast<axes::properties&>(go.get_properties ());
1161 
1162  ap.set (name, value);
1163  }
1164  }
1165  }
1166 
1167  void axis_auto (void)
1168  {
1169  octave_value_list args;
1170  if (m_fp.get_currentaxes ().ok ())
1171  {
1172  args(0) = m_fp.get_currentaxes ().as_octave_value ();
1173  args(1) = "auto";
1174  octave::feval ("axis", args);
1175  mark_modified ();
1176  }
1177  }
1178 
1179  void toggle_grid (void)
1180  {
1181  octave_value_list args;
1182  if (m_fp.get_currentaxes ().ok ())
1183  args(0) = m_fp.get_currentaxes ().as_octave_value ();
1184 
1185  octave::feval ("grid", args);
1186  mark_modified ();
1187  }
1188 
1189  void pixel2pos (const graphics_handle& ax, int px, int py, double& xx,
1190  double& yy) const
1191  {
1192  gh_manager& gh_mgr = octave::__get_gh_manager__ ("plot_window::pixel2pos");
1193 
1194  pixel2pos (gh_mgr.get_object (ax), px, py, xx, yy);
1195  }
1196 
1197  void pixel2pos (graphics_object ax, int px, int py, double& xx,
1198  double& yy) const
1199  {
1200  if (ax && ax.isa ("axes"))
1201  {
1202  axes::properties& ap
1203  = dynamic_cast<axes::properties&> (ax.get_properties ());
1204  ColumnVector pp = ap.pixel2coord (px, py);
1205  xx = pp(0);
1206  yy = pp(1);
1207  }
1208  }
1209 
1210  graphics_handle pixel2axes_or_ca (int px, int py)
1211  {
1212  Matrix kids = m_fp.get_children ();
1213  int len = kids.numel ();
1214 
1215  gh_manager& gh_mgr = octave::__get_gh_manager__ ("plot_window::pixel2axes_or_ca");
1216 
1217  for (int k = 0; k < len; k++)
1218  {
1219  graphics_handle hnd = gh_mgr.lookup (kids(k));
1220 
1221  if (hnd.ok ())
1222  {
1223  graphics_object kid = gh_mgr.get_object (hnd);
1224 
1225  if (kid.valid_object () && kid.isa ("axes"))
1226  {
1227  Matrix bb = kid.get_properties ().get_boundingbox (false);
1228 
1229  if (bb(0) <= px && px < (bb(0)+bb(2))
1230  && bb(1) <= py && py < (bb(1)+bb(3)))
1231  {
1232  return hnd;
1233  }
1234  }
1235  }
1236  }
1237  return m_fp.get_currentaxes ();
1238  }
1239 
1240  void pixel2status (const graphics_handle& ax, int px0, int py0,
1241  int px1 = -1, int py1 = -1)
1242  {
1243  gh_manager& gh_mgr = octave::__get_gh_manager__ ("plot_window::pixel2status");
1244 
1245  pixel2status (gh_mgr.get_object (ax), px0, py0, px1, py1);
1246  }
1247 
1248  void pixel2status (graphics_object ax, int px0, int py0,
1249  int px1 = -1, int py1 = -1)
1250  {
1251  double x0, y0, x1, y1;
1252  x0 = y0 = x1 = y1 = octave::numeric_limits<double>::NaN ();
1253  std::stringstream cbuf;
1254  cbuf.precision (4);
1255  cbuf.width (6);
1256  pixel2pos (ax, px0, py0, x0, y0);
1257  cbuf << '[' << x0 << ", " << y0 << ']';
1258  if (px1 >= 0)
1259  {
1260  pixel2pos (ax, px1, py1, x1, y1);
1261  cbuf << " -> ["<< x1 << ", " << y1 << ']';
1262  }
1263 
1264  m_status->value (cbuf.str ().c_str ());
1265  }
1266 
1267  void view2status (graphics_object ax)
1268  {
1269  if (ax && ax.isa ("axes"))
1270  {
1271  axes::properties& ap
1272  = dynamic_cast<axes::properties&> (ax.get_properties ());
1273  std::stringstream cbuf;
1274  cbuf.precision (4);
1275  cbuf.width (6);
1276  Matrix v (1,2,0);
1277  v = ap.get ("view").matrix_value ();
1278  cbuf << "[azimuth: " << v(0) << ", elevation: " << v(1) << ']';
1279 
1280  m_status->value (cbuf.str ().c_str ());
1281  }
1282  }
1283 
1284  void set_currentpoint (int px, int py)
1285  {
1286  if (! m_fp.is_beingdeleted ())
1287  {
1288  Matrix pos = m_fp.map_from_boundingbox (px, py);
1289  m_fp.set_currentpoint (pos);
1290 
1291  gh_manager& gh_mgr
1292  = octave::__get_gh_manager__ ("plot_window::set_currentpoint");
1293 
1294  graphics_object robj = gh_mgr.get_object (m_fp.get_parent ());
1295 
1297 
1298  = dynamic_cast<root_figure::properties&> (robj.get_properties ());
1299  rp.set_currentfigure (m_fp.get___myhandle__ ().value ());
1300  }
1301  }
1302 
1303  void set_axes_currentpoint (graphics_object ax, int px, int py)
1304  {
1305  if (ax.valid_object () && ax.isa ("axes"))
1306  {
1307  axes::properties& ap
1308  = dynamic_cast<axes::properties&> (ax.get_properties ());
1309 
1310  Matrix x_zlim = ap.get_transform_zlim ();
1311  Matrix pos (2, 3, 0.0);
1312 
1313  // front point (nearest to the viewer)
1314  ColumnVector tmp = ap.get_transform ().untransform (px, py, x_zlim(0));
1315  pos(0,0) = tmp(0);
1316  pos(0,1) = tmp(1);
1317  pos(0,2) = tmp(2);
1318 
1319  // back point (furthest from the viewer)
1320  tmp = ap.get_transform ().untransform (px, py, x_zlim(1));
1321  pos(1,0) = tmp(0);
1322  pos(1,1) = tmp(1);
1323  pos(1,2) = tmp(2);
1324 
1325  ap.set_currentpoint (pos);
1326  if (ap.get_tag () != "legend" && ap.get_tag () != "colorbar")
1327  m_fp.set_currentaxes (ap.get___myhandle__ ().value ());
1328  }
1329  }
1330 
1331  int menu_dy ()
1332  {
1333  if (m_uimenu->is_visible ())
1334  return m_menu_h;
1335  else
1336  return 0;
1337  }
1338 
1339  octave_scalar_map format_key_event (int e_key, const char *e_text, int e_state)
1340  {
1341  octave_scalar_map evt;
1342 
1343  evt.assign ("Character", octave_value (e_text));
1344  evt.assign ("Modifier", octave_value (modifier2cell (e_state)));
1345 
1346  std::string key_str;
1347  std::ostringstream tmp_str;
1348 
1349  if (e_key == FL_Escape)
1350  key_str = "escape";
1351  else if (e_key == FL_Tab)
1352  key_str = "tab";
1353  else if (e_key == FL_Caps_Lock)
1354  key_str = "capslock";
1355  else if (e_key == FL_Shift_L || e_key == FL_Shift_R)
1356  key_str = "shift";
1357  else if (e_key == FL_Control_L || e_key == FL_Control_R)
1358  key_str = "control";
1359  else if (e_key == FL_Meta_L || e_key == FL_Meta_R)
1360  key_str = "windows";
1361  else if (e_key == FL_Alt_L || e_key == FL_Alt_R)
1362  key_str = "alt";
1363  else if (e_key == 32)
1364  key_str = "space";
1365  else if (e_key == FL_Enter)
1366  key_str = "return";
1367  else if (e_key == FL_BackSpace)
1368  key_str = "backspace";
1369  else if (e_key == FL_Print)
1370  key_str = "printscreen";
1371  else if (e_key == FL_Pause)
1372  key_str = "pause";
1373  else if (e_key == FL_Home)
1374  key_str = "home";
1375  else if (e_key == FL_End)
1376  key_str = "end";
1377  else if (e_key == FL_Insert)
1378  key_str = "insert";
1379  else if (e_key == FL_Page_Up)
1380  key_str = "pageup";
1381  else if (e_key == FL_Delete)
1382  key_str = "delete";
1383  else if (e_key == FL_Page_Down)
1384  key_str = "pagedown";
1385  else if (e_key == FL_Left)
1386  key_str = "leftarrow";
1387  else if (e_key == FL_Up)
1388  key_str = "uparrow";
1389  else if (e_key == FL_Right)
1390  key_str = "rightarrow";
1391  else if (e_key == FL_Down)
1392  key_str = "downarrow";
1393  else if (e_key == FL_Num_Lock)
1394  key_str = "numlock";
1395  else if (e_key == 0xffaf)
1396  key_str = "divide";
1397  else if (e_key == 0xffaa)
1398  key_str = "multiply";
1399  else if (e_key == 0xffad)
1400  key_str = "subtract";
1401  else if (e_key == 0xffab)
1402  key_str = "add";
1403  else if (e_key == 0xff8d)
1404  key_str = "return";
1405  else if (e_key == 0xffac)
1406  key_str = "separator";
1407  else if (e_key >= 0xffb0 && e_key <= 0xffb9)
1408  {
1409  tmp_str << "numpad" << (e_key - 0xffb0);
1410  key_str = tmp_str.str ();
1411  }
1412  else if (e_key >= (FL_F + 1) && e_key <= (FL_F + 12))
1413  {
1414  tmp_str << 'f' << (e_key - FL_F);
1415  key_str = tmp_str.str ();
1416  }
1417  else if (e_key == ',')
1418  key_str = "comma";
1419  else if (e_key == '.')
1420  key_str = "period";
1421  else if (e_key == '-')
1422  key_str = "hyphen";
1423  else if (e_key == '^' || e_key == '+' || e_key == '#'
1424  || e_key == '<' || e_key == 0xfe03 /*AltGr*/)
1425  key_str = "0";
1426  else if (isalnum (e_key))
1427  key_str = std::tolower (e_key);
1428  else if (isprint (e_text[0]))
1429  key_str = "0";
1430 
1431  evt.assign ("Key", octave_value (key_str));
1432  return evt;
1433  }
1434 
1435  Cell modifier2cell (int e_state)
1436  {
1438 
1439  if (e_state & FL_SHIFT)
1440  mod.append (std::string ("shift"));
1441  if (e_state & FL_CTRL)
1442  mod.append (std::string ("control"));
1443  if (e_state & FL_ALT)
1444  mod.append (std::string ("alt"));
1445  if (e_state & FL_COMMAND)
1446  mod.append (std::string ("command"));
1447  return Cell (mod);
1448  }
1449 
1450  void resize (int xx, int yy, int ww, int hh)
1451  {
1452  Fl_Window::resize (xx, yy, ww, hh);
1453 
1454  Matrix bb (1, 4);
1455  bb(0) = xx;
1456  bb(1) = yy;
1457  bb(2) = ww;
1458  bb(3) = hh;
1459 
1460  // update outerposition
1461  m_fp.set_boundingbox (bb, false, false);
1462 
1463  // update position
1464  m_fp.set_boundingbox (outerposition2position (bb), true, false);
1465  }
1466 
1467  bool pan_enabled (void)
1468  {
1469  // Getting pan mode property:
1470  octave_value ov_pm = m_fp.get___pan_mode__ ();
1471 
1472  octave_scalar_map pm = ov_pm.scalar_map_value ();
1473 
1474  return pm.contents ("Enable").string_value () == "on";
1475  }
1476 
1477  std::string pan_mode (void)
1478  {
1479  // Getting pan mode property:
1480  octave_value ov_pm = m_fp.get___pan_mode__ ();
1481 
1482  octave_scalar_map pm = ov_pm.scalar_map_value ();
1483 
1484  return pm.contents ("Motion").string_value ();
1485  }
1486 
1487  bool rotate_enabled (void)
1488  {
1489  // Getting rotate mode property:
1490  octave_value ov_rm = m_fp.get___rotate_mode__ ();
1491 
1492  octave_scalar_map rm = ov_rm.scalar_map_value ();
1493 
1494  return rm.contents ("Enable").string_value () == "on";
1495  }
1496 
1497  int handle (int event)
1498  {
1499  if (event == FL_FOCUS)
1500  return 1;
1501 
1502  graphics_handle gh;
1503 
1504  if (! m_fp.is_beingdeleted ())
1505  {
1506  // FLTK resends keyboard events with flipped case if all
1507  // widgets rejects the event.
1508  // See Event Propagation http://www.fltk.org/doc-1.3/events.html
1509  static bool key_resent_detected = false;
1510 
1511  gh_manager& gh_mgr = octave::__get_gh_manager__ ("plot_window::handle");
1512 
1513  switch (event)
1514  {
1515  case FL_SHORTCUT:
1516  {
1517  // check if it a resent event with switched case
1518  static int last_event_key = 0;
1519  static char last_event_text = 0;
1520 
1521  int e_key = Fl::event_key ();
1522  char e_text = Fl::event_text ()[0];
1523  key_resent_detected = (e_key == last_event_key
1524  && std::tolower (last_event_text) == std::tolower (e_text)
1525  && ((islower (last_event_text) && isupper (e_text))
1526  || (isupper (last_event_text) && islower (e_text))));
1527 
1528  last_event_key = e_key;
1529  last_event_text = e_text;
1530  }
1531  break;
1532 
1533  case FL_KEYDOWN:
1534  {
1535  int e_key = Fl::event_key ();
1536  const char *e_text = Fl::event_text ();
1537  int e_state = Fl::event_state ();
1538  octave_scalar_map evt = format_key_event (e_key, e_text, e_state);
1539 
1540  m_fp.set_currentcharacter (std::string (e_text));
1541 
1542  if (! m_fp.get_keypressfcn ().isempty ()
1543  && (evt.contents ("Key").length () > 0))
1544  {
1545  // Update CurrentPoint before callback
1546  if (Fl::event_inside (m_canvas))
1547  {
1548  m_pos_x = Fl::event_x ();
1549  m_pos_y = Fl::event_y () - menu_dy ();
1550 
1551  set_currentpoint (m_pos_x, m_pos_y);
1552 
1553  gh = pixel2axes_or_ca (m_pos_x, m_pos_y);
1554 
1555  if (gh.ok ())
1556  {
1557  m_ax_obj = gh_mgr.get_object (gh);
1558  set_axes_currentpoint (m_ax_obj, m_pos_x, m_pos_y);
1559  }
1560  }
1561 
1562  m_fp.execute_keypressfcn (evt);
1563  }
1564 
1565  // Handle special keys used in toolbar
1566  switch (e_key)
1567  {
1568  case 'a':
1569  case 'A':
1570  axis_auto ();
1571  return 1;
1572 
1573  case 'g':
1574  case 'G':
1575  toggle_grid ();
1576  return 1;
1577 
1578  case 'p':
1579  case 'P':
1580  m_fp.set___mouse_mode__ ("pan");
1581  return 1;
1582 
1583  case 'r':
1584  case 'R':
1585  m_fp.set___mouse_mode__ ("rotate");
1586  return 1;
1587  }
1588  }
1589  break;
1590 
1591  case FL_KEYUP:
1592  {
1593  int e_key = Fl::event_key ();
1594  int e_state = Fl::event_state ();
1595  octave_scalar_map evt;
1596  if (key_resent_detected && Fl::event_length () == 1)
1597  {
1598  // FLTK flipped the case of Fl::event_text because no
1599  // widget wanted the FL_KEYDOWN event.
1600  char tmp_e_text[2];
1601  tmp_e_text[0] = Fl::event_text ()[0];
1602  tmp_e_text[1] = 0;
1603  // Undo the case flip
1604  if (std::islower (tmp_e_text[0]))
1605  tmp_e_text[0] = std::toupper (tmp_e_text[0]);
1606  else
1607  tmp_e_text[0] = std::tolower (tmp_e_text[0]);
1608  evt = format_key_event (e_key, tmp_e_text, e_state);
1609  }
1610  else
1611  {
1612  const char *e_text = Fl::event_text ();
1613  evt = format_key_event (e_key, e_text, e_state);
1614  }
1615 
1616  if (! m_fp.get_keyreleasefcn ().isempty ()
1617  && (evt.contents ("Key").length () > 0))
1618  m_fp.execute_keyreleasefcn (evt);
1619  return 1;
1620  }
1621  break;
1622  }
1623 
1624  // Events we only handle if they are in the canvas area.
1625  if (Fl::event_inside (m_canvas))
1626  switch (event)
1627  {
1628  case FL_MOVE:
1629  pixel2status (pixel2axes_or_ca (Fl::event_x (),
1630  Fl::event_y () - menu_dy ()),
1631  Fl::event_x (), Fl::event_y () - menu_dy ());
1632  return 1;
1633 
1634  case FL_PUSH:
1635  m_pos_x = Fl::event_x ();
1636  m_pos_y = Fl::event_y () - menu_dy ();
1637 
1638  set_currentpoint (m_pos_x, m_pos_y);
1639 
1640  if (Fl::event_clicks ())
1641  m_fp.set_selectiontype ("open");
1642  else if (Fl::event_button () == FL_MIDDLE_MOUSE
1643  || (Fl::event_button () == FL_LEFT_MOUSE
1644  && Fl::event_shift ()))
1645  m_fp.set_selectiontype ("extend");
1646  else if (Fl::event_button () == FL_RIGHT_MOUSE
1647  || (Fl::event_button () == FL_LEFT_MOUSE
1648  && Fl::event_ctrl ()))
1649  m_fp.set_selectiontype ("alt");
1650  else
1651  m_fp.set_selectiontype ("normal");
1652 
1653  gh = pixel2axes_or_ca (m_pos_x, m_pos_y);
1654 
1655  if (gh.ok ())
1656  {
1657  m_ax_obj = gh_mgr.get_object (gh);
1658  set_axes_currentpoint (m_ax_obj, m_pos_x, m_pos_y);
1659  }
1660 
1661  // Ensure windowbuttondownfcn is called after currentpoint
1662  // is updated but before calling buttondownfcn.
1663  if (! m_fp.get_windowbuttondownfcn ().isempty ())
1664  m_fp.execute_windowbuttondownfcn (Fl::event_button ());
1665 
1666  if (gh.ok ())
1667  {
1668  m_fp.set_currentobject (m_ax_obj.get_handle ().value ());
1669 
1670  base_properties& props = m_ax_obj.get_properties ();
1671  if (! props.get_buttondownfcn ().isempty ())
1672  props.execute_buttondownfcn (Fl::event_button ());
1673 
1674  return 1;
1675  }
1676  else if (! m_fp.get_buttondownfcn ().isempty ())
1677  m_fp.execute_buttondownfcn (Fl::event_button ());
1678 
1679  break;
1680 
1681  case FL_DRAG:
1682  if (! m_fp.get_windowbuttonmotionfcn ().isempty ())
1683  {
1684  set_currentpoint (Fl::event_x (), Fl::event_y () - menu_dy ());
1685  m_fp.execute_windowbuttonmotionfcn ();
1686  }
1687 
1688  if (Fl::event_button () == 1)
1689  {
1690  if (m_ax_obj && m_ax_obj.isa ("axes"))
1691  {
1692  axes::properties& ap = dynamic_cast<axes::properties&> (m_ax_obj.get_properties ());
1693 
1694  // Don't pan or rotate legend
1695  if (ap.get_tag () != "legend")
1696  {
1697  if (rotate_enabled ())
1698  view2status (m_ax_obj);
1699  else
1700  pixel2status (m_ax_obj, m_pos_x, m_pos_y,
1701  Fl::event_x (),
1702  Fl::event_y () - menu_dy ());
1703 
1704  double x0, y0, x1, y1;
1705  Matrix pos = m_fp.get_boundingbox (true);
1706  pixel2pos (m_ax_obj, m_pos_x, m_pos_y, x0, y0);
1707  pixel2pos (m_ax_obj, Fl::event_x (),
1708  Fl::event_y () - menu_dy (),
1709  x1, y1);
1710 
1711  if (pan_enabled ())
1712  {
1713  std::string mode = pan_mode ();
1714 
1715  ap.translate_view (mode, x0, x1, y0, y1);
1716  }
1717  else if (rotate_enabled ())
1718  {
1719  double daz, del;
1720  daz = (Fl::event_x () - m_pos_x) / pos(2) * 360;
1721  del = (Fl::event_y () - menu_dy () - m_pos_y)
1722  / pos(3) * 360;
1723  ap.rotate_view (del, daz);
1724  }
1725  }
1726  else
1727  {
1728  // move the position of the legend
1729  Matrix pos = ap.get_position ().matrix_value ();
1730  pos(0) += double (Fl::event_x () - m_pos_x)
1731  / m_canvas->w ();
1732  pos(1) -= double (Fl::event_y () - menu_dy () - m_pos_y)
1733  / m_canvas->h ();
1734  ap.set_position (pos);
1735  }
1736 
1737  m_pos_x = Fl::event_x ();
1738  m_pos_y = Fl::event_y () - menu_dy ();
1739  mark_modified ();
1740  }
1741  return 1;
1742  }
1743  else if (Fl::event_button () == 3)
1744  {
1745  pixel2status (m_ax_obj, m_pos_x, m_pos_y,
1746  Fl::event_x (), Fl::event_y () - menu_dy ());
1747  Matrix zoom_box (1,4,0);
1748  zoom_box(0) = m_pos_x;
1749  zoom_box(1) = m_pos_y;
1750  zoom_box(2) = Fl::event_x ();
1751  zoom_box(3) = Fl::event_y () - menu_dy ();
1752  m_canvas->set_zoom_box (zoom_box);
1753  m_canvas->zoom (true);
1754  mark_modified ();
1755  return 1;
1756  }
1757 
1758  break;
1759 
1760  case FL_MOUSEWHEEL:
1761  {
1762  graphics_object ax
1763  = gh_mgr.get_object (pixel2axes_or_ca (Fl::event_x (),
1764  Fl::event_y ()
1765  - menu_dy ()));
1766  if (ax && ax.isa ("axes"))
1767  {
1768  axes::properties& ap
1769  = dynamic_cast<axes::properties&> (ax.get_properties ());
1770 
1771  // Control how fast to zoom when using scroll wheel.
1772  double wheel_zoom_speed = ap.get_mousewheelzoom ();
1773 
1774  // Determine if we're zooming in or out.
1775  const double factor = (Fl::event_dy () < 0
1776  ? 1 / (1.0 - wheel_zoom_speed)
1777  : 1.0 - wheel_zoom_speed);
1778 
1779  // Get the point we're zooming about.
1780  double x1, y1;
1781  pixel2pos (ax, Fl::event_x (), Fl::event_y () - menu_dy (),
1782  x1, y1);
1783 
1784  // FIXME: should we only zoom about point for 2D plots?
1785 
1786  ap.zoom_about_point ("both", x1, y1, factor, false);
1787  mark_modified ();
1788  return 1;
1789  }
1790  }
1791 
1792  break;
1793 
1794  case FL_RELEASE:
1795  if (! m_fp.get_windowbuttonupfcn ().isempty ())
1796  {
1797  set_currentpoint (Fl::event_x (), Fl::event_y () - menu_dy ());
1798  m_fp.execute_windowbuttonupfcn ();
1799  }
1800 
1801  if ((Fl::event_button () == 1) && Fl::event_clicks ())
1802  {
1803  // Double click
1804  set_on_ax_obj ("xlimmode", "auto");
1805  set_on_ax_obj ("ylimmode", "auto");
1806  set_on_ax_obj ("zlimmode", "auto");
1807  mark_modified ();
1808  return 1;
1809  }
1810  if (Fl::event_button () == 3)
1811  {
1812  // End of drag -- zoom.
1813  if (m_canvas->zoom ())
1814  {
1815  m_canvas->zoom (false);
1816  double x0,y0,x1,y1;
1817  if (m_ax_obj && m_ax_obj.isa ("axes"))
1818  {
1819  axes::properties& ap = dynamic_cast<axes::properties&>
1820  (m_ax_obj.get_properties ());
1821  pixel2pos (m_ax_obj, m_pos_x, m_pos_y, x0, y0);
1822  int pos_x1 = Fl::event_x ();
1823  int pos_y1 = Fl::event_y () - menu_dy ();
1824  pixel2pos (m_ax_obj, pos_x1, pos_y1, x1, y1);
1825  Matrix xl (1,2,0);
1826  Matrix yl (1,2,0);
1827  int dx = abs (m_pos_x - pos_x1);
1828  int dy = abs (m_pos_y - pos_y1);
1829  // Smallest zoom box must be 4 pixels square
1830  if ((dx > 4) && (dy > 4))
1831  {
1832  if (x0 < x1)
1833  {
1834  xl(0) = x0;
1835  xl(1) = x1;
1836  }
1837  else
1838  {
1839  xl(0) = x1;
1840  xl(1) = x0;
1841  }
1842  if (y0 < y1)
1843  {
1844  yl(0) = y0;
1845  yl(1) = y1;
1846  }
1847  else
1848  {
1849  yl(0) = y1;
1850  yl(1) = y0;
1851  }
1852  ap.zoom ("both", xl, yl);
1853  }
1854  mark_modified ();
1855  return 1;
1856  }
1857  }
1858  }
1859  break;
1860  }
1861  }
1862 
1863  return Fl_Window::handle (event);
1864  }
1865 };
1866 
1867 class figure_manager
1868 {
1869 private:
1870 
1871  figure_manager (void) = default;
1872 
1873 public:
1874 
1875  // No copying!
1876 
1877  figure_manager (const figure_manager&) = delete;
1878 
1879  figure_manager& operator = (const figure_manager&) = delete;
1880 
1881  ~figure_manager (void)
1882  {
1883  close_all ();
1884  }
1885 
1886  static bool instance_ok (void)
1887  {
1888  bool retval = true;
1889 
1890  if (! instance)
1891  instance = new figure_manager ();
1892 
1893  return retval;
1894  }
1895 
1896  static void close_all (void)
1897  {
1898  if (instance_ok ())
1899  instance->do_close_all ();
1900  }
1901 
1902  static void new_window (figure::properties& fp)
1903  {
1904  if (instance_ok ())
1905  instance->do_new_window (fp);
1906  }
1907 
1908  static void delete_window (int idx)
1909  {
1910  if (instance_ok ())
1911  instance->do_delete_window (idx);
1912  }
1913 
1914  static void delete_window (const std::string& idx_str)
1915  {
1916  delete_window (str2idx (idx_str));
1917  }
1918 
1919  static void renumber_figure (const std::string& idx_str, double new_number)
1920  {
1921  if (instance_ok ())
1922  instance->do_renumber_figure (str2idx (idx_str), new_number);
1923  }
1924 
1925  static void toggle_window_visibility (int idx, bool is_visible)
1926  {
1927  if (instance_ok ())
1928  instance->do_toggle_window_visibility (idx, is_visible);
1929  }
1930 
1931  static void toggle_window_visibility (const std::string& idx_str,
1932  bool is_visible)
1933  {
1934  toggle_window_visibility (str2idx (idx_str), is_visible);
1935  }
1936 
1937  static void mark_modified (int idx)
1938  {
1939  if (instance_ok ())
1940  instance->do_mark_modified (idx);
1941  }
1942 
1943  static void mark_modified (const graphics_handle& gh)
1944  {
1945  mark_modified (hnd2idx (gh));
1946  }
1947 
1948  static void set_name (int idx)
1949  {
1950  if (instance_ok ())
1951  instance->do_set_name (idx);
1952  }
1953 
1954  static void set_name (const std::string& idx_str)
1955  {
1956  set_name (str2idx (idx_str));
1957  }
1958 
1959  static Matrix get_size (int idx)
1960  {
1961  return instance_ok () ? instance->do_get_size (idx) : Matrix ();
1962  }
1963 
1964  static Matrix get_size (const graphics_handle& gh)
1965  {
1966  return get_size (hnd2idx (gh));
1967  }
1968 
1969  static void print (const graphics_handle& gh, const std::string& cmd,
1970  const std::string& term)
1971  {
1972  if (instance_ok ())
1973  instance->do_print (hnd2idx (gh), cmd, term);
1974  }
1975 
1976  static uint8NDArray get_pixels (const graphics_handle& gh)
1977  {
1979  if (instance_ok ())
1980  retval = instance->do_get_pixels (hnd2idx (gh));
1981 
1982  return retval;
1983  }
1984 
1985  static void uimenu_update (const graphics_handle& figh,
1986  const graphics_handle& uimenuh, int id)
1987  {
1988  if (instance_ok ())
1989  instance->do_uimenu_update (hnd2idx (figh), uimenuh, id);
1990  }
1991 
1992  static void update_canvas (const graphics_handle& gh,
1993  const graphics_handle& ca)
1994  {
1995  if (instance_ok ())
1996  instance->do_update_canvas (hnd2idx (gh), ca);
1997  }
1998 
1999  static void update_boundingbox (const std::string& fig_idx_str,
2000  bool internal)
2001  {
2002  if (instance_ok ())
2003  instance->do_update_boundingbox (str2idx (fig_idx_str), internal);
2004  }
2005 
2006  static void toggle_menubar_visibility (const std::string& fig_idx_str,
2007  bool menubar_is_figure)
2008  {
2009  if (instance_ok ())
2010  instance->do_toggle_menubar_visibility (str2idx (fig_idx_str),
2011  menubar_is_figure);
2012  }
2013 
2014 private:
2015 
2016  static figure_manager *instance;
2017 
2018  // Singleton -- hide all of the above.
2019 
2020  static int curr_index;
2021 
2022  typedef std::map<int, plot_window*> window_map;
2023 
2024  typedef window_map::iterator wm_iterator;;
2025 
2026  window_map windows;
2027 
2028  static std::string fltk_idx_header;
2029 
2030  void do_close_all (void)
2031  {
2032  wm_iterator win;
2033  for (win = windows.begin (); win != windows.end (); win++)
2034  delete win->second;
2035  windows.clear ();
2036  }
2037 
2038  void do_new_window (figure::properties& fp)
2039  {
2040  int idx = figprops2idx (fp);
2041 
2042  if (idx >= 0 && windows.find (idx) == windows.end ())
2043  {
2044  Matrix pos = fp.get_outerposition ().matrix_value ();
2045  bool internal = false;
2046  // check if figure::properties::outerposition is default -1.0
2047  if (pos(2) != -1.0 && pos(3) != -1.0)
2048  {
2049  pos = fp.get_boundingbox (internal);
2050  }
2051  else
2052  {
2053  // use position
2054  internal = true;
2055  pos = fp.get_boundingbox (internal);
2056  }
2057 
2058  idx2figprops (curr_index, fp);
2059 
2060  windows[curr_index++] = new plot_window (pos(0), pos(1), pos(2), pos(3),
2061  fp, internal);
2062  }
2063  }
2064 
2065  void do_delete_window (int idx)
2066  {
2067  wm_iterator win = windows.find (idx);
2068 
2069  if (win != windows.end ())
2070  {
2071  delete win->second;
2072  windows.erase (win);
2073  }
2074  }
2075 
2076  void do_renumber_figure (int idx, double new_number)
2077  {
2078  wm_iterator win = windows.find (idx);
2079 
2080  if (win != windows.end ())
2081  win->second->renumber (new_number);
2082  }
2083 
2084  void do_toggle_window_visibility (int idx, bool is_visible)
2085  {
2086  wm_iterator win = windows.find (idx);
2087 
2088  if (win != windows.end ())
2089  {
2090  if (is_visible)
2091  {
2092  win->second->show ();
2093  win->second->show_canvas ();
2094  }
2095  else
2096  win->second->hide ();
2097 
2098  }
2099  }
2100 
2101  void do_toggle_menubar_visibility (int fig_idx, bool menubar_is_figure)
2102  {
2103  wm_iterator win = windows.find (fig_idx);
2104 
2105  if (win != windows.end ())
2106  {
2107  if (menubar_is_figure)
2108  win->second->show_menubar ();
2109  else
2110  win->second->hide_menubar ();
2111 
2112  win->second->redraw ();
2113  }
2114  }
2115 
2116  void do_mark_modified (int idx)
2117  {
2118  wm_iterator win = windows.find (idx);
2119 
2120  if (win != windows.end ())
2121  {
2122  win->second->mark_modified ();
2123  }
2124  }
2125 
2126  void do_set_name (int idx)
2127  {
2128  wm_iterator win = windows.find (idx);
2129 
2130  if (win != windows.end ())
2131  win->second->set_name ();
2132  }
2133 
2134  Matrix do_get_size (int idx)
2135  {
2136  Matrix sz (1, 2, 0.0);
2137 
2138  wm_iterator win = windows.find (idx);
2139 
2140  if (win != windows.end ())
2141  {
2142  sz(0) = win->second->w ();
2143  sz(1) = win->second->h ();
2144  }
2145 
2146  return sz;
2147  }
2148 
2149  void do_print (int idx, const std::string& cmd, const std::string& term)
2150  {
2151  wm_iterator win = windows.find (idx);
2152 
2153  if (win != windows.end ())
2154  win->second->print (cmd, term);
2155  }
2156 
2157  uint8NDArray do_get_pixels (int idx)
2158  {
2160  wm_iterator win = windows.find (idx);
2161 
2162  if (win != windows.end ())
2163  retval = win->second->get_pixels ();
2164 
2165  return retval;
2166  }
2167 
2168  void do_uimenu_update (int idx, const graphics_handle& gh, int id)
2169  {
2170  wm_iterator win = windows.find (idx);
2171 
2172  if (win != windows.end ())
2173  win->second->uimenu_update (gh, id);
2174  }
2175 
2176  void do_update_canvas (int idx, const graphics_handle& ca)
2177  {
2178  wm_iterator win = windows.find (idx);
2179 
2180  if (win != windows.end ())
2181  {
2182  if (ca.ok ())
2183  win->second->show_canvas ();
2184  else
2185  win->second->hide_canvas ();
2186  }
2187  }
2188 
2189  void do_update_boundingbox (int idx, bool internal)
2190  {
2191  wm_iterator win = windows.find (idx);
2192 
2193  if (win != windows.end ())
2194  win->second->update_boundingbox (internal);
2195  }
2196 
2197  static int str2idx (const caseless_str& clstr)
2198  {
2199  int ind;
2200  if (clstr.find (fltk_idx_header,0) == 0)
2201  {
2202  std::istringstream istr (clstr.substr (fltk_idx_header.size ()));
2203  if (istr >> ind)
2204  return ind;
2205  }
2206 
2207  error ("figure_manager: could not recognize fltk index");
2208  }
2209 
2210  void idx2figprops (int idx, figure::properties& fp)
2211  {
2212  std::ostringstream ind_str;
2213  ind_str << fltk_idx_header << idx;
2214  fp.set___plot_stream__ (ind_str.str ());
2215  }
2216 
2217  static int figprops2idx (const figure::properties& fp)
2218  {
2219  if (fp.get___graphics_toolkit__ () == FLTK_GRAPHICS_TOOLKIT_NAME)
2220  {
2221  octave_value ps = fp.get___plot_stream__ ();
2222  if (ps.is_string ())
2223  return str2idx (ps.string_value ());
2224  else
2225  return 0;
2226  }
2227 
2228  error ("figure_manager: figure is not fltk");
2229  }
2230 
2231  static int hnd2idx (double h)
2232  {
2233  gh_manager& gh_mgr = octave::__get_gh_manager__ ("figure_manager::hnd2idx");
2234 
2235  graphics_object fobj = gh_mgr.get_object (h);
2236 
2237  if (fobj && fobj.isa ("figure"))
2238  {
2239  figure::properties& fp
2240  = dynamic_cast<figure::properties&> (fobj.get_properties ());
2241  return figprops2idx (fp);
2242  }
2243 
2244  error ("figure_manager: H (= %g) is not a figure", h);
2245  }
2246 
2247  static int hnd2idx (const graphics_handle& fh)
2248  {
2249  return hnd2idx (fh.value ());
2250  }
2251 };
2252 
2253 figure_manager *figure_manager::instance = nullptr;
2254 
2255 std::string figure_manager::fltk_idx_header="fltk index=";
2256 int figure_manager::curr_index = 1;
2257 
2258 static bool toolkit_loaded = false;
2259 
2260 class fltk_graphics_toolkit : public octave::base_graphics_toolkit
2261 {
2262 public:
2263 
2264  fltk_graphics_toolkit (octave::interpreter& interp)
2265  : octave::base_graphics_toolkit (FLTK_GRAPHICS_TOOLKIT_NAME),
2266  m_interpreter (interp), input_event_hook_fcn_id ()
2267  {
2268  Fl::visual (FL_RGB);
2269  }
2270 
2271  ~fltk_graphics_toolkit (void) = default;
2272 
2273  bool is_valid (void) const { return true; }
2274 
2275  bool initialize (const graphics_object& go)
2276  {
2277  if (go.isa ("figure")
2278  || go.isa ("uimenu"))
2279  {
2280  if (go.isa ("uimenu"))
2281  update (go, uimenu::properties::ID_LABEL);
2282 
2283  return true;
2284  }
2285 
2286  return false;
2287  }
2288 
2289  void finalize (const graphics_object& go)
2290  {
2291  if (go.isa ("figure"))
2292  {
2293  octave_value ov = go.get (caseless_str ("__plot_stream__"));
2294 
2295  if (! ov.isempty ())
2296  figure_manager::delete_window (ov.string_value ());
2297  }
2298  }
2299 
2300  void uimenu_set___fltk_label__ (graphics_object uimenu_obj)
2301  {
2302  if (uimenu_obj.valid_object ())
2303  {
2304  uimenu::properties& uimenup
2305  = dynamic_cast<uimenu::properties&> (uimenu_obj.get_properties ());
2306  std::string fltk_label = uimenup.get_label ();
2307 
2308  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
2309 
2310  graphics_object go = gh_mgr.get_object (uimenu_obj.get_parent ());
2311 
2312  if (go.isa ("uimenu"))
2313  fltk_label = dynamic_cast<const uimenu::properties&>
2314  (go.get_properties ()).get___fltk_label__ ()
2315  + '/'
2316  + fltk_label;
2317  else if (go.isa ("figure") || go.isa ("uicontextmenu"))
2318  ;
2319  else
2320  error ("invalid parent object\n");
2321 
2322  uimenup.set___fltk_label__ (fltk_label);
2323  }
2324  }
2325 
2326  void update (const graphics_object& go, int id)
2327  {
2328  if (go.isa ("figure"))
2329  {
2330  octave_value ov = go.get (caseless_str ("__plot_stream__"));
2331 
2332  if (! ov.isempty ())
2333  {
2334  const figure::properties& fp
2335  = dynamic_cast<const figure::properties&> (go.get_properties ());
2336 
2337  switch (id)
2338  {
2339  case base_properties::ID_VISIBLE:
2340  figure_manager::toggle_window_visibility (ov.string_value (),
2341  fp.is_visible ());
2342  break;
2343 
2344  case figure::properties::ID_MENUBAR:
2345  figure_manager::toggle_menubar_visibility
2346  (ov.string_value (), fp.menubar_is ("figure"));
2347  break;
2348 
2349  case figure::properties::ID_CURRENTAXES:
2350  figure_manager::update_canvas (go.get_handle (),
2351  fp.get_currentaxes ());
2352  break;
2353 
2354  case figure::properties::ID_NAME:
2355  case figure::properties::ID_NUMBERTITLE:
2356  figure_manager::set_name (ov.string_value ());
2357  break;
2358 
2359  case figure::properties::ID_INTEGERHANDLE:
2360  {
2361  std::string tmp = ov.string_value ();
2362  graphics_handle gh = fp.get___myhandle__ ();
2363  figure_manager::renumber_figure (tmp, gh.value ());
2364  figure_manager::set_name (tmp);
2365  }
2366  break;
2367 
2368  case figure::properties::ID_POSITION:
2369  figure_manager::update_boundingbox (ov.string_value (), true);
2370  break;
2371 
2372  case figure::properties::ID_OUTERPOSITION:
2373  figure_manager::update_boundingbox (ov.string_value (), false);
2374  break;
2375  }
2376  }
2377  }
2378  else if (go.isa ("uimenu"))
2379  {
2380  if (id == uimenu::properties::ID_LABEL)
2381  uimenu_set___fltk_label__ (go);
2382 
2383  graphics_object fig = go.get_ancestor ("figure");
2384  figure_manager::uimenu_update (fig.get_handle (), go.get_handle (), id);
2385  }
2386  }
2387 
2388  void redraw_figure (const graphics_object& go) const
2389  {
2390  // We scan all figures and add those which use FLTK.
2391 
2392  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
2393 
2394  graphics_object obj = gh_mgr.get_object (0);
2395 
2396  if (obj && obj.isa ("root"))
2397  {
2398  base_properties& props = obj.get_properties ();
2399  Matrix children = props.get_all_children ();
2400 
2401  for (octave_idx_type n = 0; n < children.numel (); n++)
2402  {
2403  graphics_object fobj = gh_mgr.get_object (children (n));
2404 
2405  if (fobj && fobj.isa ("figure"))
2406  {
2407  figure::properties& fp
2408  = dynamic_cast<figure::properties&> (fobj.get_properties ());
2409 
2410  if (fp.get___graphics_toolkit__ ()
2411  == FLTK_GRAPHICS_TOOLKIT_NAME)
2412  figure_manager::new_window (fp);
2413  }
2414  }
2415  }
2416 
2417  figure_manager::mark_modified (go.get_handle ());
2418  Fl::check ();
2419  }
2420 
2421  void print_figure (const graphics_object& go,
2422  const std::string& term,
2423  const std::string& file_cmd,
2424  const std::string& /*debug_file*/) const
2425  {
2426  figure_manager::print (go.get_handle (), file_cmd, term);
2427  }
2428 
2429  uint8NDArray get_pixels (const graphics_object& go) const
2430  {
2431  return figure_manager::get_pixels (go.get_handle ());
2432  }
2433 
2434  Matrix get_canvas_size (const graphics_handle& fh) const
2435  {
2436  return figure_manager::get_size (fh);
2437  }
2438 
2439 /*
2440  double get_screen_resolution (void) const
2441  {
2442  // FLTK doesn't give this info.
2443  return 72.0;
2444 
2445  // FIXME: FLTK >= 1.3.0 could do this with Fl::screen_dpi (h, v, n)
2446  // but do we need it?
2447  }
2448 */
2449 
2450  Matrix get_screen_size (void) const
2451  {
2452  Matrix sz (1, 2, 0.0);
2453  sz(0) = Fl::w ();
2454  sz(1) = Fl::h ();
2455  return sz;
2456  }
2457 
2458  void close (void)
2459  {
2460  if (toolkit_loaded)
2461  {
2462  m_interpreter.munlock ("__init_fltk__");
2463 
2464  octave_value_list args = input_event_hook_fcn_id;
2465  args.append (false);
2466  Fremove_input_event_hook (m_interpreter, args, 0);
2467  input_event_hook_fcn_id = octave_value_list ();
2468 
2469  figure_manager::close_all ();
2470  }
2471  }
2472 
2473  void set_input_event_hook_id (const octave_value_list& id)
2474  {
2475  input_event_hook_fcn_id = id;
2476  }
2477 
2478 private:
2479 
2480  octave::interpreter& m_interpreter;
2481 
2482  octave_value_list input_event_hook_fcn_id;
2483 };
2484 
2485 #endif
2486 
2487 // This function has the same type signature as a builtin function so
2488 // that we can create a function handle from it but it is not defined
2489 // using a DEFMETHOD macro so that it will be omitted from the list of
2490 // autoloaded functions in the PKG_ADD file and will not be visible to
2491 // the interpreter.
2492 
2493 static octave_value_list
2495 {
2496 #if defined (HAVE_FLTK)
2497  Fl::check ();
2498 
2499  if (Vdrawnow_requested)
2500  Fdrawnow (interp);
2501 
2502  return octave_value_list ();
2503 #else
2504  octave_unused_parameter (interp);
2505 
2506  err_disabled_feature ("__fltk_check__", "OpenGL and FLTK");
2507 #endif
2508 }
2509 
2510 // Initialize the fltk graphics toolkit.
2511 
2512 DEFMETHOD_DLD (__init_fltk__, interp, , ,
2513  doc: /* -*- texinfo -*-
2514 @deftypefn {} {} __init_fltk__ ()
2515 Undocumented internal function.
2516 @end deftypefn */)
2517 {
2518 #if defined (HAVE_FLTK)
2519  octave::display_info& dpy_info = interp.get_display_info ();
2520 
2521  if (! dpy_info.display_available ())
2522  error ("__init_fltk__: no graphics DISPLAY available");
2523  else if (! toolkit_loaded)
2524  {
2525  interp.mlock ();
2526 
2527  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
2528 
2529  fltk_graphics_toolkit *fltk = new fltk_graphics_toolkit (interp);
2530  octave::graphics_toolkit tk (fltk);
2531  gtk_mgr.load_toolkit (tk);
2532  toolkit_loaded = true;
2533 
2535  octave_value fcn_handle (new octave_fcn_handle (fcn));
2536 
2537  octave_value_list id = Fadd_input_event_hook (interp, fcn_handle, 1);
2538 
2539  fltk->set_input_event_hook_id (id);
2540  }
2541 
2542  return octave_value_list ();
2543 
2544 #else
2545  octave_unused_parameter (interp);
2546 
2547  err_disabled_feature ("__init_fltk__", "OpenGL and FLTK");
2548 #endif
2549 }
2550 
2551 /*
2552 ## No test needed for internal helper function.
2553 %!assert (1)
2554 */
#define NaN
Definition: Faddeeva.cc:248
static octave_value_list F__fltk_check__(octave::interpreter &interp, const octave_value_list &, int)
Array< octave_idx_type > sort_rows_idx(sortmode mode=ASCENDING) const
Sort by rows returns only indices.
Definition: Array.cc:2066
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
Array< octave_idx_type > find(octave_idx_type n=-1, bool backward=false) const
Find indices of (at most n) nonzero elements.
Definition: Array.cc:2225
void clear(void)
Definition: Array.cc:87
Definition: Cell.h:43
Definition: dMatrix.h:42
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:151
void rotate_view(double delta_az, double delta_el, bool push_to_zoom_stack=true)
Definition: graphics.cc:9157
void zoom(const std::string &mode, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8940
graphics_xform get_transform(void) const
Definition: graphics.in.h:3480
ColumnVector pixel2coord(double px, double py) const
Definition: graphics.in.h:3530
void zoom_about_point(const std::string &mode, double x, double y, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8911
Matrix get_transform_zlim(void) const
Definition: graphics.in.h:3487
void translate_view(const std::string &mode, double x0, double x1, double y0, double y1, bool push_to_zoom_stack=true)
Definition: graphics.cc:9064
Matrix get_children(void) const
Definition: graphics.in.h:2292
Matrix get_all_children(void) const
Definition: graphics.in.h:2297
virtual void set(const caseless_str &, const octave_value &)
virtual octave_value get(const caseless_str &pname) const
virtual property get_property(const caseless_str &pname)
virtual Matrix get_boundingbox(bool=false, const Matrix &=Matrix()) const
Definition: graphics.in.h:2268
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:4254
Matrix map_from_boundingbox(double x, double y) const
Definition: graphics.cc:4297
void set_boundingbox(const Matrix &bb, bool internal=false, bool do_notify_toolkit=true)
Definition: graphics.cc:4284
graphics_object get_object(double val) const
Definition: graphics.in.h:6260
graphics_handle lookup(double val) const
Definition: graphics.in.h:6246
octave_value get(bool all=false) const
Definition: graphics.in.h:2746
graphics_object get_ancestor(const std::string &type) const
Definition: graphics.cc:3905
bool isa(const std::string &go_name) const
Definition: graphics.in.h:2827
base_properties & get_properties(void)
Definition: graphics.in.h:2829
graphics_handle get_handle(void) const
Definition: graphics.in.h:2815
graphics_handle get_parent(void) const
Definition: graphics.in.h:2813
bool valid_object(void) const
Definition: graphics.in.h:2847
ColumnVector untransform(double x, double y, double z, bool use_scale=true) const
Definition: graphics.cc:7391
virtual Matrix get_canvas_size(const graphics_handle &) const
virtual void update(const graphics_object &, int)
virtual bool is_valid(void) const
virtual uint8NDArray get_pixels(const graphics_object &) const
virtual Matrix get_screen_size(void) const
virtual void redraw_figure(const graphics_object &) const
virtual void print_figure(const graphics_object &, const std::string &, const std::string &, const std::string &="") const
virtual void finalize(const graphics_object &)
virtual bool initialize(const graphics_object &)
bool display_available(void) const
Definition: display.h:68
void load_toolkit(const graphics_toolkit &tk)
Definition: gtk-manager.h:57
virtual void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
Definition: oct-opengl.h:454
virtual void glLoadIdentity(void)
Definition: oct-opengl.h:274
virtual void glMatrixMode(GLenum mode)
Definition: oct-opengl.h:289
virtual void draw(const graphics_object &go, bool toplevel=true)
Definition: gl-render.cc:717
virtual void draw_zoom_box(int width, int height, int x1, int y1, int x2, int y2, const Matrix &overlaycolor, double overlayalpha, const Matrix &bordercolor, double borderalpha, double borderwidth)
Definition: gl-render.cc:1153
static std::string getenv(const std::string &name)
Definition: oct-env.cc:271
double value(void) const
Definition: oct-handle.h:78
bool ok(void) const
Definition: oct-handle.h:113
const octave_value & contents(const_iterator p) const
Definition: oct-map.h:205
void assign(const std::string &k, const octave_value &val)
Definition: oct-map.h:238
octave_value_list & append(const octave_value &val)
Definition: ovl.cc:98
octave_idx_type length(void) const
bool is_string(void) const
Definition: ov.h:593
std::string string_value(bool force=false) const
Definition: ov.h:927
octave_scalar_map scalar_map_value(void) const
bool isempty(void) const
Definition: ov.h:557
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:806
bool set(const octave_value &val, bool do_run=true, bool do_notify_toolkit=true)
Definition: graphics.in.h:2003
string_property callback_property callback
Definition: graphics.in.h:5385
#define DEFMETHOD_DLD(name, interp_name, args_name, nargout_name, doc)
Macro to define an at run time dynamically loadable builtin method.
Definition: defun-dld.h:99
void error(const char *fmt,...)
Definition: error.cc:968
#define panic_impossible()
Definition: error.h:380
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:53
OCTAVE_EXPORT octave_value_list Fdrawnow(octave::interpreter &interp, const octave_value_list &args, int)
Definition: graphics.cc:13698
QString name
bool Vdrawnow_requested
Definition: input.cc:90
T octave_idx_type m
Definition: mx-inlines.cc:773
octave_idx_type n
Definition: mx-inlines.cc:753
T * r
Definition: mx-inlines.cc:773
std::complex< double > w(std::complex< double > z, double relerr=0)
static std::string pan_mode(const graphics_object figObj)
Definition: Canvas.cc:304
static bool pan_enabled(const graphics_object figObj)
Definition: Canvas.cc:292
std::complex< T > floor(const std::complex< T > &x)
Definition: lo-mappers.h:130
gh_manager & __get_gh_manager__(const std::string &who)
void gl2ps_print(opengl_functions &glfcns, const graphics_object &fig, const std::string &stream, const std::string &term)
octave_value_list feval(const char *name, const octave_value_list &args, int nargout)
Evaluate an Octave function (built-in or interpreted) and return the list of result values.
Definition: oct-parse.cc:9580
static octave_idx_type get_size(double d, const std::string &who)
Definition: oct-stream.cc:127
octave_int< T > mod(const octave_int< T > &x, const octave_int< T > &y)
Definition: oct-inttypes.h:932
@ DESCENDING
Definition: oct-sort.h:95
void draw(QDomElement &parent_elt, pdfpainter &painter)
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
static T abs(T x)
Definition: pr-output.cc:1678
F77_RET_T len
Definition: xerbla.cc:61