GNU Octave 10.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-2025 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
1559 m_fp.set_currentcharacter (std::string (e_text));
1560
1561 if (! m_fp.get_keypressfcn ().isempty ()
1562 && (evt.contents ("Key").length () > 0))
1563 {
1564 // Update CurrentPoint before callback
1565 if (Fl::event_inside (m_canvas))
1566 {
1567 m_pos_x = Fl::event_x ();
1568 m_pos_y = Fl::event_y () - menu_dy ();
1569
1570 set_currentpoint (m_pos_x, m_pos_y);
1571
1572 gh = pixel2axes_or_ca (m_pos_x, m_pos_y);
1573
1574 if (gh.ok ())
1575 {
1576 m_ax_obj = gh_mgr.get_object (gh);
1577 set_axes_currentpoint (m_ax_obj, m_pos_x, m_pos_y);
1578 }
1579 }
1580
1581 m_fp.execute_keypressfcn (evt);
1582 }
1583
1584 // Handle special keys used in toolbar
1585 switch (e_key)
1586 {
1587 case 'a':
1588 case 'A':
1589 axis_auto ();
1590 return 1;
1591
1592 case 'g':
1593 case 'G':
1594 toggle_grid ();
1595 return 1;
1596
1597 case 'p':
1598 case 'P':
1599 m_fp.set___mouse_mode__ ("pan");
1600 return 1;
1601
1602 case 'r':
1603 case 'R':
1604 m_fp.set___mouse_mode__ ("rotate");
1605 return 1;
1606 }
1607 }
1608 break;
1609
1610 case FL_KEYUP:
1611 {
1612 int e_key = Fl::event_key ();
1613 int e_state = Fl::event_state ();
1615 if (key_resent_detected && Fl::event_length () == 1)
1616 {
1617 // FLTK flipped the case of Fl::event_text because no
1618 // widget wanted the FL_KEYDOWN event.
1619 char tmp_e_text[2];
1620 tmp_e_text[0] = Fl::event_text ()[0];
1621 tmp_e_text[1] = 0;
1622 // Undo the case flip
1623 if (std::islower (tmp_e_text[0]))
1624 tmp_e_text[0] = std::toupper (tmp_e_text[0]);
1625 else
1626 tmp_e_text[0] = std::tolower (tmp_e_text[0]);
1627 evt = format_key_event (e_key, tmp_e_text, e_state);
1628 }
1629 else
1630 {
1631 const char *e_text = Fl::event_text ();
1632 evt = format_key_event (e_key, e_text, e_state);
1633 }
1634
1635 if (! m_fp.get_keyreleasefcn ().isempty ()
1636 && (evt.contents ("Key").length () > 0))
1637 m_fp.execute_keyreleasefcn (evt);
1638 return 1;
1639 }
1640 break;
1641 }
1642
1643 // Events we only handle if they are in the canvas area.
1644 if (Fl::event_inside (m_canvas))
1645 switch (event)
1646 {
1647 case FL_MOVE:
1648 pixel2status (pixel2axes_or_ca (Fl::event_x (),
1649 Fl::event_y () - menu_dy ()),
1650 Fl::event_x (), Fl::event_y () - menu_dy ());
1651 return 1;
1652
1653 case FL_PUSH:
1654 m_pos_x = Fl::event_x ();
1655 m_pos_y = Fl::event_y () - menu_dy ();
1656
1657 set_currentpoint (m_pos_x, m_pos_y);
1658
1659 if (Fl::event_clicks ())
1660 m_fp.set_selectiontype ("open");
1661 else if (Fl::event_button () == FL_MIDDLE_MOUSE
1662 || (Fl::event_button () == FL_LEFT_MOUSE
1663 && Fl::event_shift ()))
1664 m_fp.set_selectiontype ("extend");
1665 else if (Fl::event_button () == FL_RIGHT_MOUSE
1666 || (Fl::event_button () == FL_LEFT_MOUSE
1667 && Fl::event_ctrl ()))
1668 m_fp.set_selectiontype ("alt");
1669 else
1670 m_fp.set_selectiontype ("normal");
1671
1672 gh = pixel2axes_or_ca (m_pos_x, m_pos_y);
1673
1674 if (gh.ok ())
1675 {
1676 m_ax_obj = gh_mgr.get_object (gh);
1677 set_axes_currentpoint (m_ax_obj, m_pos_x, m_pos_y);
1678 }
1679
1680 // Ensure windowbuttondownfcn is called after currentpoint
1681 // is updated but before calling buttondownfcn.
1682 if (! m_fp.get_windowbuttondownfcn ().isempty ())
1683 m_fp.execute_windowbuttondownfcn (Fl::event_button ());
1684
1685 if (gh.ok ())
1686 {
1687 m_fp.set_currentobject (m_ax_obj.get_handle ().value ());
1688
1689 base_properties& props = m_ax_obj.get_properties ();
1690 if (! props.get_buttondownfcn ().isempty ())
1691 props.execute_buttondownfcn (Fl::event_button ());
1692
1693 return 1;
1694 }
1695 else if (! m_fp.get_buttondownfcn ().isempty ())
1696 m_fp.execute_buttondownfcn (Fl::event_button ());
1697
1698 break;
1699
1700 case FL_DRAG:
1701 if (! m_fp.get_windowbuttonmotionfcn ().isempty ())
1702 {
1703 set_currentpoint (Fl::event_x (), Fl::event_y () - menu_dy ());
1704 m_fp.execute_windowbuttonmotionfcn ();
1705 }
1706
1707 if (Fl::event_button () == 1)
1708 {
1709 if (m_ax_obj && m_ax_obj.isa ("axes"))
1710 {
1711 axes::properties& ap = dynamic_cast<axes::properties&> (m_ax_obj.get_properties ());
1712
1713 // Don't pan or rotate legend
1714 if (ap.get_tag () != "legend")
1715 {
1716 if (rotate_enabled ())
1717 view2status (m_ax_obj);
1718 else
1719 pixel2status (m_ax_obj, m_pos_x, m_pos_y,
1720 Fl::event_x (),
1721 Fl::event_y () - menu_dy ());
1722
1723 double x0, y0, x1, y1;
1724 Matrix pos = m_fp.get_boundingbox (true);
1725 pixel2pos (m_ax_obj, m_pos_x, m_pos_y, x0, y0);
1726 pixel2pos (m_ax_obj, Fl::event_x (),
1727 Fl::event_y () - menu_dy (),
1728 x1, y1);
1729
1730 if (pan_enabled ())
1731 {
1732 std::string mode = pan_mode ();
1733
1734 ap.translate_view (mode, x0, x1, y0, y1);
1735 }
1736 else if (rotate_enabled ())
1737 {
1738 double daz, del;
1739 daz = (Fl::event_x () - m_pos_x) / pos(2) * 360;
1740 del = (Fl::event_y () - menu_dy () - m_pos_y)
1741 / pos(3) * 360;
1742 ap.rotate_view (del, daz);
1743 }
1744 }
1745 else
1746 {
1747 // move the position of the legend
1748 Matrix pos = ap.get_position ().matrix_value ();
1749 pos(0) += double (Fl::event_x () - m_pos_x)
1750 / m_canvas->w ();
1751 pos(1) -= double (Fl::event_y () - menu_dy () - m_pos_y)
1752 / m_canvas->h ();
1753 ap.set_position (pos);
1754 }
1755
1756 m_pos_x = Fl::event_x ();
1757 m_pos_y = Fl::event_y () - menu_dy ();
1758 mark_modified ();
1759 }
1760 return 1;
1761 }
1762 else if (Fl::event_button () == 3)
1763 {
1764 pixel2status (m_ax_obj, m_pos_x, m_pos_y,
1765 Fl::event_x (), Fl::event_y () - menu_dy ());
1766 Matrix zoom_box (1, 4, 0);
1767 zoom_box(0) = m_pos_x;
1768 zoom_box(1) = m_pos_y;
1769 zoom_box(2) = Fl::event_x ();
1770 zoom_box(3) = Fl::event_y () - menu_dy ();
1771 m_canvas->set_zoom_box (zoom_box);
1772 m_canvas->zoom (true);
1773 mark_modified ();
1774 return 1;
1775 }
1776
1777 break;
1778
1779 case FL_MOUSEWHEEL:
1780 {
1781 graphics_object ax
1782 = gh_mgr.get_object (pixel2axes_or_ca (Fl::event_x (),
1783 Fl::event_y ()
1784 - menu_dy ()));
1785 if (ax && ax.isa ("axes"))
1786 {
1787 axes::properties& ap
1788 = dynamic_cast<axes::properties&> (ax.get_properties ());
1789
1790 // Control how fast to zoom when using scroll wheel.
1791 double wheel_zoom_speed = ap.get_mousewheelzoom ();
1792
1793 // Determine if we're zooming in or out.
1794 const double factor = (Fl::event_dy () < 0
1795 ? 1 / (1.0 - wheel_zoom_speed)
1796 : 1.0 - wheel_zoom_speed);
1797
1798 // Get the point we're zooming about.
1799 double x1, y1;
1800 pixel2pos (ax, Fl::event_x (), Fl::event_y () - menu_dy (),
1801 x1, y1);
1802
1803 // FIXME: should we only zoom about point for 2D plots?
1804
1805 ap.zoom_about_point ("both", x1, y1, factor, false);
1806 mark_modified ();
1807 return 1;
1808 }
1809 }
1810
1811 break;
1812
1813 case FL_RELEASE:
1814 if (! m_fp.get_windowbuttonupfcn ().isempty ())
1815 {
1816 set_currentpoint (Fl::event_x (),
1817 Fl::event_y () - menu_dy ());
1818 m_fp.execute_windowbuttonupfcn ();
1819 }
1820
1821 if ((Fl::event_button () == 1) && Fl::event_clicks ())
1822 {
1823 // Double click
1824 set_on_ax_obj ("xlimmode", "auto");
1825 set_on_ax_obj ("ylimmode", "auto");
1826 set_on_ax_obj ("zlimmode", "auto");
1827 mark_modified ();
1828 return 1;
1829 }
1830 if (Fl::event_button () == 3)
1831 {
1832 // End of drag -- zoom.
1833 if (m_canvas->zoom ())
1834 {
1835 m_canvas->zoom (false);
1836 double x0, y0, x1, y1;
1837 if (m_ax_obj && m_ax_obj.isa ("axes"))
1838 {
1839 axes::properties& ap = dynamic_cast<axes::properties&>
1840 (m_ax_obj.get_properties ());
1841 pixel2pos (m_ax_obj, m_pos_x, m_pos_y, x0, y0);
1842 int pos_x1 = Fl::event_x ();
1843 int pos_y1 = Fl::event_y () - menu_dy ();
1844 pixel2pos (m_ax_obj, pos_x1, pos_y1, x1, y1);
1845 Matrix xl (1, 2, 0);
1846 Matrix yl (1, 2, 0);
1847 int dx = abs (m_pos_x - pos_x1);
1848 int dy = abs (m_pos_y - pos_y1);
1849 // Smallest zoom box must be 4 pixels square
1850 if ((dx > 4) && (dy > 4))
1851 {
1852 if (x0 < x1)
1853 {
1854 xl(0) = x0;
1855 xl(1) = x1;
1856 }
1857 else
1858 {
1859 xl(0) = x1;
1860 xl(1) = x0;
1861 }
1862 if (y0 < y1)
1863 {
1864 yl(0) = y0;
1865 yl(1) = y1;
1866 }
1867 else
1868 {
1869 yl(0) = y1;
1870 yl(1) = y0;
1871 }
1872 ap.zoom ("both", xl, yl);
1873 }
1874 mark_modified ();
1875 return 1;
1876 }
1877 }
1878 }
1879 break;
1880 }
1881 }
1882
1883 return Fl_Window::handle (event);
1884 }
1885};
1886
1887class figure_manager
1888{
1889private:
1890
1891 figure_manager () = default;
1892
1893public:
1894
1895 OCTAVE_DISABLE_COPY_MOVE (figure_manager)
1896
1897 ~figure_manager ()
1898 {
1899 close_all ();
1900 }
1901
1902 static bool instance_ok ()
1903 {
1904 bool retval = true;
1905
1906 if (! s_instance)
1907 s_instance = new figure_manager ();
1908
1909 return retval;
1910 }
1911
1912 static void close_all ()
1913 {
1914 if (instance_ok ())
1915 s_instance->do_close_all ();
1916 }
1917
1918 static void new_window (figure::properties& fp)
1919 {
1920 if (instance_ok ())
1921 s_instance->do_new_window (fp);
1922 }
1923
1924 static void delete_window (int idx)
1925 {
1926 if (instance_ok ())
1927 s_instance->do_delete_window (idx);
1928 }
1929
1930 static void delete_window (const std::string& idx_str)
1931 {
1932 delete_window (str2idx (idx_str));
1933 }
1934
1935 static void renumber_figure (const std::string& idx_str, double new_number)
1936 {
1937 if (instance_ok ())
1938 s_instance->do_renumber_figure (str2idx (idx_str), new_number);
1939 }
1940
1941 static void toggle_window_visibility (int idx, bool is_visible)
1942 {
1943 if (instance_ok ())
1944 s_instance->do_toggle_window_visibility (idx, is_visible);
1945 }
1946
1947 static void toggle_window_visibility (const std::string& idx_str,
1948 bool is_visible)
1949 {
1950 toggle_window_visibility (str2idx (idx_str), is_visible);
1951 }
1952
1953 static void mark_modified (int idx)
1954 {
1955 if (instance_ok ())
1956 s_instance->do_mark_modified (idx);
1957 }
1958
1959 static void mark_modified (const graphics_handle& gh)
1960 {
1961 mark_modified (hnd2idx (gh));
1962 }
1963
1964 static void set_name (int idx)
1965 {
1966 if (instance_ok ())
1967 s_instance->do_set_name (idx);
1968 }
1969
1970 static void set_name (const std::string& idx_str)
1971 {
1972 set_name (str2idx (idx_str));
1973 }
1974
1975 static Matrix get_size (int idx)
1976 {
1977 return instance_ok () ? s_instance->do_get_size (idx) : Matrix ();
1978 }
1979
1980 static Matrix get_size (const graphics_handle& gh)
1981 {
1982 return get_size (hnd2idx (gh));
1983 }
1984
1985 static void print (const graphics_handle& gh, const std::string& cmd,
1986 const std::string& term)
1987 {
1988 if (instance_ok ())
1989 s_instance->do_print (hnd2idx (gh), cmd, term);
1990 }
1991
1992 static uint8NDArray get_pixels (const graphics_handle& gh)
1993 {
1994 uint8NDArray retval;
1995 if (instance_ok ())
1996 retval = s_instance->do_get_pixels (hnd2idx (gh));
1997
1998 return retval;
1999 }
2000
2001 static void uimenu_update (const graphics_handle& figh,
2002 const graphics_handle& uimenuh, int id)
2003 {
2004 if (instance_ok ())
2005 s_instance->do_uimenu_update (hnd2idx (figh), uimenuh, id);
2006 }
2007
2008 static void update_canvas (const graphics_handle& gh,
2009 const graphics_handle& ca)
2010 {
2011 if (instance_ok ())
2012 s_instance->do_update_canvas (hnd2idx (gh), ca);
2013 }
2014
2015 static void update_boundingbox (const std::string& fig_idx_str,
2016 bool internal)
2017 {
2018 if (instance_ok ())
2019 s_instance->do_update_boundingbox (str2idx (fig_idx_str), internal);
2020 }
2021
2022 static void toggle_menubar_visibility (const std::string& fig_idx_str,
2023 bool menubar_is_figure)
2024 {
2025 if (instance_ok ())
2026 s_instance->do_toggle_menubar_visibility (str2idx (fig_idx_str),
2027 menubar_is_figure);
2028 }
2029
2030private:
2031
2032 static figure_manager *s_instance;
2033
2034 // Singleton -- hide all of the above.
2035
2036 static int s_curr_index;
2037
2038 typedef std::map<int, plot_window *> window_map;
2039
2040 typedef window_map::iterator wm_iterator;
2041
2042 window_map m_windows;
2043
2044 static std::string s_fltk_idx_header;
2045
2046 void do_close_all ()
2047 {
2048 for (auto& win : m_windows)
2049 delete win.second;
2050 m_windows.clear ();
2051 }
2052
2053 void do_new_window (figure::properties& fp)
2054 {
2055 int idx = figprops2idx (fp);
2056
2057 if (idx >= 0 && m_windows.find (idx) == m_windows.end ())
2058 {
2059 Matrix pos = fp.get_outerposition ().matrix_value ();
2060 bool internal = false;
2061 // check if figure::properties::outerposition is default -1.0
2062 if (pos(2) != -1.0 && pos(3) != -1.0)
2063 {
2064 pos = fp.get_boundingbox (internal);
2065 }
2066 else
2067 {
2068 // use position
2069 internal = true;
2070 pos = fp.get_boundingbox (internal);
2071 }
2072
2073 idx2figprops (s_curr_index, fp);
2074
2075 m_windows[s_curr_index++] = new plot_window (pos(0), pos(1), pos(2), pos(3),
2076 fp, internal);
2077 }
2078 }
2079
2080 void do_delete_window (int idx)
2081 {
2082 wm_iterator win = m_windows.find (idx);
2083
2084 if (win != m_windows.end ())
2085 {
2086 delete win->second;
2087 m_windows.erase (win);
2088 }
2089 }
2090
2091 void do_renumber_figure (int idx, double new_number)
2092 {
2093 wm_iterator win = m_windows.find (idx);
2094
2095 if (win != m_windows.end ())
2096 win->second->renumber (new_number);
2097 }
2098
2099 void do_toggle_window_visibility (int idx, bool is_visible)
2100 {
2101 wm_iterator win = m_windows.find (idx);
2102
2103 if (win != m_windows.end ())
2104 {
2105 if (is_visible)
2106 {
2107 win->second->show ();
2108 win->second->show_canvas ();
2109 }
2110 else
2111 win->second->hide ();
2112
2113 }
2114 }
2115
2116 void do_toggle_menubar_visibility (int fig_idx, bool menubar_is_figure)
2117 {
2118 wm_iterator win = m_windows.find (fig_idx);
2119
2120 if (win != m_windows.end ())
2121 {
2122 if (menubar_is_figure)
2123 win->second->show_menubar ();
2124 else
2125 win->second->hide_menubar ();
2126
2127 win->second->redraw ();
2128 }
2129 }
2130
2131 void do_mark_modified (int idx)
2132 {
2133 wm_iterator win = m_windows.find (idx);
2134
2135 if (win != m_windows.end ())
2136 {
2137 win->second->mark_modified ();
2138 }
2139 }
2140
2141 void do_set_name (int idx)
2142 {
2143 wm_iterator win = m_windows.find (idx);
2144
2145 if (win != m_windows.end ())
2146 win->second->set_name ();
2147 }
2148
2149 Matrix do_get_size (int idx)
2150 {
2151 Matrix sz (1, 2, 0.0);
2152
2153 wm_iterator win = m_windows.find (idx);
2154
2155 if (win != m_windows.end ())
2156 {
2157 sz(0) = win->second->w ();
2158 sz(1) = win->second->h ();
2159 }
2160
2161 return sz;
2162 }
2163
2164 void do_print (int idx, const std::string& cmd, const std::string& term)
2165 {
2166 wm_iterator win = m_windows.find (idx);
2167
2168 if (win != m_windows.end ())
2169 win->second->print (cmd, term);
2170 }
2171
2172 uint8NDArray do_get_pixels (int idx)
2173 {
2174 uint8NDArray retval;
2175 wm_iterator win = m_windows.find (idx);
2176
2177 if (win != m_windows.end ())
2178 retval = win->second->get_pixels ();
2179
2180 return retval;
2181 }
2182
2183 void do_uimenu_update (int idx, const graphics_handle& gh, int id)
2184 {
2185 wm_iterator win = m_windows.find (idx);
2186
2187 if (win != m_windows.end ())
2188 win->second->uimenu_update (gh, id);
2189 }
2190
2191 void do_update_canvas (int idx, const graphics_handle& ca)
2192 {
2193 wm_iterator win = m_windows.find (idx);
2194
2195 if (win != m_windows.end ())
2196 {
2197 if (ca.ok ())
2198 win->second->show_canvas ();
2199 else
2200 win->second->hide_canvas ();
2201 }
2202 }
2203
2204 void do_update_boundingbox (int idx, bool internal)
2205 {
2206 wm_iterator win = m_windows.find (idx);
2207
2208 if (win != m_windows.end ())
2209 win->second->update_boundingbox (internal);
2210 }
2211
2212 static int str2idx (const caseless_str& clstr)
2213 {
2214 int ind;
2215 if (clstr.find (s_fltk_idx_header, 0) == 0)
2216 {
2217 std::istringstream istr (clstr.substr (s_fltk_idx_header.size ()));
2218 if (istr >> ind)
2219 return ind;
2220 }
2221
2222 error ("figure_manager: could not recognize fltk index");
2223 }
2224
2225 void idx2figprops (int idx, figure::properties& fp)
2226 {
2227 std::ostringstream ind_str;
2228 ind_str << s_fltk_idx_header << idx;
2229 fp.set___plot_stream__ (ind_str.str ());
2230 }
2231
2232 static int figprops2idx (const figure::properties& fp)
2233 {
2234 if (fp.get___graphics_toolkit__ () == FLTK_GRAPHICS_TOOLKIT_NAME)
2235 {
2236 octave_value ps = fp.get___plot_stream__ ();
2237 if (ps.is_string ())
2238 return str2idx (ps.string_value ());
2239 else
2240 return 0;
2241 }
2242
2243 error ("figure_manager: figure is not fltk");
2244 }
2245
2246 static int hnd2idx (double h)
2247 {
2248 gh_manager& gh_mgr = octave::__get_gh_manager__ ();
2249
2250 graphics_object fobj = gh_mgr.get_object (h);
2251
2252 if (fobj && fobj.isa ("figure"))
2253 {
2254 figure::properties& fp
2255 = dynamic_cast<figure::properties&> (fobj.get_properties ());
2256 return figprops2idx (fp);
2257 }
2258
2259 error ("figure_manager: H (= %g) is not a figure", h);
2260 }
2261
2262 static int hnd2idx (const graphics_handle& fh)
2263 {
2264 return hnd2idx (fh.value ());
2265 }
2266};
2267
2268figure_manager *figure_manager::s_instance = nullptr;
2269
2270std::string figure_manager::s_fltk_idx_header="fltk index=";
2271int figure_manager::s_curr_index = 1;
2272
2273static bool toolkit_loaded = false;
2274
2275class fltk_graphics_toolkit : public octave::base_graphics_toolkit
2276{
2277public:
2278
2279 fltk_graphics_toolkit (octave::interpreter& interp)
2280 : octave::base_graphics_toolkit (FLTK_GRAPHICS_TOOLKIT_NAME),
2281 m_interpreter (interp), m_input_event_hook_fcn_id ()
2282 {
2283 static bool warned = false;
2284
2285 if (! warned)
2286 {
2288 ("Octave:fltk-graphics",
2289 "using the fltk graphics toolkit is discouraged\n\
2290\n\
2291The FLTK graphics toolkit is not actively maintained and has a number\n\
2292of limitations that are unlikely to be fixed.\n\
2293The qt toolkit is recommended instead.\n");
2294
2295 warned = true;
2296 }
2297
2298 Fl::visual (FL_RGB);
2299 }
2300
2301 OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (fltk_graphics_toolkit)
2302
2303 ~fltk_graphics_toolkit () = default;
2304
2305 bool is_valid () const { return true; }
2306
2307 bool initialize (const graphics_object& go)
2308 {
2309 if (go.isa ("figure")
2310 || go.isa ("uimenu"))
2311 {
2312 if (go.isa ("uimenu"))
2313 update (go, uimenu::properties::ID_LABEL);
2314
2315 return true;
2316 }
2317
2318 return false;
2319 }
2320
2321 void finalize (const graphics_object& go)
2322 {
2323 if (go.isa ("figure"))
2324 {
2325 octave_value ov = go.get (caseless_str ("__plot_stream__"));
2326
2327 if (! ov.isempty ())
2328 figure_manager::delete_window (ov.string_value ());
2329 }
2330 }
2331
2332 void uimenu_set___fltk_label__ (graphics_object uimenu_obj)
2333 {
2334 if (uimenu_obj.valid_object ())
2335 {
2336 uimenu::properties& uimenup
2337 = dynamic_cast<uimenu::properties&> (uimenu_obj.get_properties ());
2338 std::string fltk_label = uimenup.get_label ();
2339
2340 gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
2341
2342 graphics_object go = gh_mgr.get_object (uimenu_obj.get_parent ());
2343
2344 if (go.isa ("uimenu"))
2345 fltk_label = dynamic_cast<const uimenu::properties&>
2346 (go.get_properties ()).get___fltk_label__ ()
2347 + '/'
2348 + fltk_label;
2349 else if (go.isa ("figure") || go.isa ("uicontextmenu"))
2350 ;
2351 else
2352 error ("invalid parent object\n");
2353
2354 uimenup.set___fltk_label__ (fltk_label);
2355 }
2356 }
2357
2358 void update (const graphics_object& go, int id)
2359 {
2360 if (go.isa ("figure"))
2361 {
2362 octave_value ov = go.get (caseless_str ("__plot_stream__"));
2363
2364 if (! ov.isempty ())
2365 {
2366 const figure::properties& fp
2367 = dynamic_cast<const figure::properties&> (go.get_properties ());
2368
2369 switch (id)
2370 {
2371 case base_properties::ID_VISIBLE:
2372 figure_manager::toggle_window_visibility (ov.string_value (),
2373 fp.is_visible ());
2374 break;
2375
2376 case figure::properties::ID_MENUBAR:
2377 figure_manager::toggle_menubar_visibility
2378 (ov.string_value (), fp.menubar_is ("figure"));
2379 break;
2380
2381 case figure::properties::ID_CURRENTAXES:
2382 figure_manager::update_canvas (go.get_handle (),
2383 fp.get_currentaxes ());
2384 break;
2385
2386 case figure::properties::ID_NAME:
2387 case figure::properties::ID_NUMBERTITLE:
2388 figure_manager::set_name (ov.string_value ());
2389 break;
2390
2391 case figure::properties::ID_INTEGERHANDLE:
2392 {
2393 std::string tmp = ov.string_value ();
2394 graphics_handle gh = fp.get___myhandle__ ();
2395 figure_manager::renumber_figure (tmp, gh.value ());
2396 figure_manager::set_name (tmp);
2397 }
2398 break;
2399
2400 case figure::properties::ID_POSITION:
2401 figure_manager::update_boundingbox (ov.string_value (), true);
2402 break;
2403
2404 case figure::properties::ID_OUTERPOSITION:
2405 figure_manager::update_boundingbox (ov.string_value (), false);
2406 break;
2407 }
2408 }
2409 }
2410 else if (go.isa ("uimenu"))
2411 {
2412 if (id == uimenu::properties::ID_LABEL)
2413 uimenu_set___fltk_label__ (go);
2414
2415 graphics_object fig = go.get_ancestor ("figure");
2416 figure_manager::uimenu_update (fig.get_handle (), go.get_handle (), id);
2417 }
2418 }
2419
2420 void redraw_figure (const graphics_object& go) const
2421 {
2422 // We scan all figures and add those which use FLTK.
2423
2424 gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
2425
2426 graphics_object obj = gh_mgr.get_object (0);
2427
2428 if (obj && obj.isa ("root"))
2429 {
2430 base_properties& props = obj.get_properties ();
2431 Matrix children = props.get_all_children ();
2432
2433 for (octave_idx_type n = 0; n < children.numel (); n++)
2434 {
2435 graphics_object fobj = gh_mgr.get_object (children (n));
2436
2437 if (fobj && fobj.isa ("figure"))
2438 {
2439 figure::properties& fp
2440 = dynamic_cast<figure::properties&> (fobj.get_properties ());
2441
2442 if (fp.get___graphics_toolkit__ ()
2443 == FLTK_GRAPHICS_TOOLKIT_NAME)
2444 figure_manager::new_window (fp);
2445 }
2446 }
2447 }
2448
2449 figure_manager::mark_modified (go.get_handle ());
2450 Fl::check ();
2451 }
2452
2453 void print_figure (const graphics_object& go,
2454 const std::string& term,
2455 const std::string& file_cmd,
2456 const std::string& /*debug_file*/) const
2457 {
2458 figure_manager::print (go.get_handle (), file_cmd, term);
2459 }
2460
2461 uint8NDArray get_pixels (const graphics_object& go) const
2462 {
2463 return figure_manager::get_pixels (go.get_handle ());
2464 }
2465
2466 Matrix get_canvas_size (const graphics_handle& fh) const
2467 {
2468 return figure_manager::get_size (fh);
2469 }
2470
2471 /*
2472 double get_screen_resolution () const
2473 {
2474 // FLTK doesn't give this info.
2475 return 72.0;
2476
2477 // FIXME: FLTK >= 1.3.0 could do this with Fl::screen_dpi (h, v, n)
2478 // but do we need it?
2479 }
2480 */
2481
2482 Matrix get_screen_size () const
2483 {
2484 Matrix sz (1, 2, 0.0);
2485 sz(0) = Fl::w ();
2486 sz(1) = Fl::h ();
2487 return sz;
2488 }
2489
2490 void close ()
2491 {
2492 if (toolkit_loaded)
2493 {
2494 m_interpreter.munlock ("__init_fltk__");
2495
2496 octave_value_list args = m_input_event_hook_fcn_id;
2497 args.append (false);
2498 Fremove_input_event_hook (m_interpreter, args);
2499 m_input_event_hook_fcn_id = octave_value_list ();
2500
2501 figure_manager::close_all ();
2502 }
2503 }
2504
2505 void set_input_event_hook_id (const octave_value_list& id)
2506 {
2507 m_input_event_hook_fcn_id = id;
2508 }
2509
2510private:
2511
2512 octave::interpreter& m_interpreter;
2513
2514 octave_value_list m_input_event_hook_fcn_id;
2515};
2516
2517#endif
2518
2519DEFMETHOD_DLD (__fltk_check__, interp, , ,
2520 doc: /* -*- texinfo -*-
2521@deftypefn {} {} __fltk_check__ ()
2522Undocumented internal function. Calls Fl::check ()
2523@end deftypefn */)
2524{
2525#if defined (HAVE_FLTK)
2526 Fl::check ();
2527
2529 Fdrawnow (interp);
2530
2531 return octave_value_list ();
2532#else
2533 octave_unused_parameter (interp);
2534
2535 err_disabled_feature ("__fltk_check__", "OpenGL and FLTK");
2536#endif
2537}
2538
2539// Initialize the fltk graphics toolkit.
2540
2541DEFMETHOD_DLD (__init_fltk__, interp, , ,
2542 doc: /* -*- texinfo -*-
2543@deftypefn {} {} __init_fltk__ ()
2544Undocumented internal function.
2545@end deftypefn */)
2546{
2547#if defined (HAVE_FLTK)
2548 octave::display_info& dpy_info = interp.get_display_info ();
2549
2550 if (! dpy_info.display_available ())
2551 error ("__init_fltk__: no graphics DISPLAY available");
2552 else if (! toolkit_loaded)
2553 {
2554 interp.mlock ();
2555
2556 octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
2557
2558 fltk_graphics_toolkit *fltk = new fltk_graphics_toolkit (interp);
2559 octave::graphics_toolkit tk (fltk);
2560 gtk_mgr.load_toolkit (tk);
2561 toolkit_loaded = true;
2562
2564 octave_value fcn_handle (new octave_fcn_handle (fcn));
2565
2566 octave_value_list id = Fadd_input_event_hook (interp,
2567 ovl (fcn_handle), 1);
2568
2569 fltk->set_input_event_hook_id (id);
2570 }
2571
2572 return octave_value_list ();
2573
2574#else
2575 octave_unused_parameter (interp);
2576
2577 err_disabled_feature ("__init_fltk__", "OpenGL and FLTK");
2578#endif
2579}
2580
2581/*
2582## No test needed for internal helper function.
2583%!assert (1)
2584*/
2585
2586OCTAVE_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.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.h:418
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:637
bool isempty() const
Definition ov.h:601
std::string string_value(bool force=false) const
Definition ov.h:983
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:1093
void error(const char *fmt,...)
Definition error.cc:1003
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 lo-mappers.h:294
@ 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