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