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