Mercurial > octave
changeset 31663:a74935a6cc75
move gh_manager class out of graphics.in.h and graphics.cc to separate files
* gh-manager.h, gh-manager.cc: New files. Move gh_manager class here
from graphics.in.h and graphics.cc.
* gl-render.cc, gl2ps-print.cc, interpreter.h, graphics.cc,
graphics-utils.cc, graphics-toolkit.cc: Include gh-manager.h.
* graphics.in.h, graphics.cc (make_graphics_object_from_type):
Declare extern.
* libinterp/corefcn/module.mk: Update.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Sat, 10 Dec 2022 00:30:20 -0500 |
parents | a36035adefeb |
children | a1e7e0b62765 |
files | libinterp/corefcn/gh-manager.cc libinterp/corefcn/gh-manager.h libinterp/corefcn/gl-render.cc libinterp/corefcn/gl2ps-print.cc libinterp/corefcn/graphics-toolkit.cc libinterp/corefcn/graphics-utils.cc libinterp/corefcn/graphics.cc libinterp/corefcn/graphics.in.h libinterp/corefcn/interpreter.h libinterp/corefcn/module.mk |
diffstat | 10 files changed, 1009 insertions(+), 916 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/gh-manager.cc Sat Dec 10 00:30:20 2022 -0500 @@ -0,0 +1,706 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2007-2022 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// Octave is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Octave is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include "cmd-edit.h" + +#include "builtin-defun-decls.h" +#include "gh-manager.h" +#include "graphics-utils.h" +#include "input.h" +#include "interpreter-private.h" +#include "interpreter.h" +#include "parse.h" + +OCTAVE_BEGIN_NAMESPACE(octave) + +static double +make_handle_fraction (void) +{ + static double maxrand = RAND_MAX + 2.0; + + return (rand () + 1.0) / maxrand; +} + +graphics_handle +gh_manager::get_handle (bool integer_figure_handle) +{ + graphics_handle retval; + + if (integer_figure_handle) + { + // Figure handles are positive integers corresponding + // to the figure number. + + // We always want the lowest unused figure number. + + retval = 1; + + while (m_handle_map.find (retval) != m_handle_map.end ()) + retval++; + } + else + { + // Other graphics handles are negative integers plus some random + // fractional part. To avoid running out of integers, we recycle the + // integer part but tack on a new random part each time. + + auto p = m_handle_free_list.begin (); + + if (p != m_handle_free_list.end ()) + { + retval = *p; + m_handle_free_list.erase (p); + } + else + { + retval = graphics_handle (m_next_handle); + + m_next_handle = std::ceil (m_next_handle) - 1.0 - make_handle_fraction (); + } + } + + return retval; +} + +void +gh_manager::free (const graphics_handle& h, bool from_root) +{ + if (h.ok ()) + { + if (h.value () == 0) + error ("graphics_handle::free: can't delete root object"); + + auto p = m_handle_map.find (h); + + if (p == m_handle_map.end ()) + error ("graphics_handle::free: invalid object %g", h.value ()); + + base_properties& bp = p->second.get_properties (); + + if (! p->second.valid_object () || bp.is_beingdeleted ()) + return; + + graphics_handle parent_h = p->second.get_parent (); + graphics_object parent_go = nullptr; + if (! from_root || isfigure (h.value ())) + parent_go = get_object (parent_h); + + bp.set_beingdeleted (true); + + // delete listeners before invalidating object + p->second.remove_all_listeners (); + + bp.delete_children (true, from_root); + + // NOTE: Call the delete function while the object's state is still valid. + octave_value val = bp.get_deletefcn (); + + bp.execute_deletefcn (); + + // Notify graphics toolkit. + p->second.finalize (); + + + // NOTE: Call remove_child before erasing the go from the map if not + // removing from groot. + // A callback function might have already deleted the parent + if ((! from_root || isfigure (h.value ())) && parent_go.valid_object () + && h.ok ()) + parent_go.remove_child (h); + + // Note: this will be valid only for first explicitly deleted + // object. All its children will then have an + // unknown graphics toolkit. + + // Graphics handles for non-figure objects are negative + // integers plus some random fractional part. To avoid + // running out of integers, we recycle the integer part + // but tack on a new random part each time. + + m_handle_map.erase (p); + + if (h.value () < 0) + m_handle_free_list.insert + (std::ceil (h.value ()) - make_handle_fraction ()); + } +} + +void +gh_manager::renumber_figure (const graphics_handle& old_gh, + const graphics_handle& new_gh) +{ + auto p = m_handle_map.find (old_gh); + + if (p == m_handle_map.end ()) + error ("graphics_handle::free: invalid object %g", old_gh.value ()); + + graphics_object go = p->second; + + m_handle_map.erase (p); + + m_handle_map[new_gh] = go; + + if (old_gh.value () < 0) + m_handle_free_list.insert (std::ceil (old_gh.value ()) + - make_handle_fraction ()); + + for (auto& hfig : m_figure_list) + { + if (hfig == old_gh) + { + hfig = new_gh; + break; + } + } +} + +void +gh_manager::close_all_figures (void) +{ + // FIXME: should we process or discard pending events? + + m_event_queue.clear (); + + // Don't use m_figure_list_iterator because we'll be removing elements + // from the list elsewhere. + + Matrix hlist = figure_handle_list (true); + + for (octave_idx_type i = 0; i < hlist.numel (); i++) + { + graphics_handle h = lookup (hlist(i)); + + if (h.ok ()) + close_figure (h); + } + + // They should all be closed now. If not, force them to close. + + hlist = figure_handle_list (true); + + for (octave_idx_type i = 0; i < hlist.numel (); i++) + { + graphics_handle h = lookup (hlist(i)); + + if (h.ok ()) + force_close_figure (h); + } + + // None left now, right? + + hlist = figure_handle_list (true); + + if (hlist.numel () != 0) + warning ("gh_manager::close_all_figures: some graphics elements failed to close"); + + // Clear all callback objects from our list. + + m_callback_objects.clear (); +} + +// We use a random value for the handle to avoid issues with plots and +// scalar values for the first argument. +gh_manager::gh_manager (octave::interpreter& interp) + : m_interpreter (interp), m_handle_map (), m_handle_free_list (), + m_next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)), + m_figure_list (), m_graphics_lock (), m_event_queue (), + m_callback_objects (), m_event_processing (0) +{ + m_handle_map[0] = graphics_object (new root_figure ()); + + octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ (); + + // Make sure the default graphics toolkit is registered. + gtk_mgr.default_toolkit (); +} + +graphics_handle +gh_manager::make_graphics_handle (const std::string& go_name, + const graphics_handle& p, + bool integer_figure_handle, + bool call_createfcn, bool notify_toolkit) +{ + graphics_handle h = get_handle (integer_figure_handle); + + base_graphics_object *bgo = make_graphics_object_from_type (go_name, h, p); + + if (! bgo) + error ("gh_manager::make_graphics_handle: invalid object type '%s'", + go_name.c_str ()); + + graphics_object go (bgo); + + m_handle_map[h] = go; + + if (go_name == "axes") + { + // Handle defaults for labels since overriding defaults for + // them can't work before the axes object is fully + // constructed. + + axes::properties& props + = dynamic_cast<axes::properties&> (go.get_properties ()); + + graphics_object tgo; + + tgo = get_object (props.get_xlabel ()); + tgo.override_defaults (); + + tgo = get_object (props.get_ylabel ()); + tgo.override_defaults (); + + tgo = get_object (props.get_zlabel ()); + tgo.override_defaults (); + + tgo = get_object (props.get_title ()); + tgo.override_defaults (); + } + + // Overriding defaults will work now because the handle is valid + // and we can find parent objects (not just handles). + go.override_defaults (); + + if (call_createfcn) + bgo->get_properties ().execute_createfcn (); + + // Notify graphics toolkit. + if (notify_toolkit) + go.initialize (); + + return h; +} + +graphics_handle +gh_manager::make_figure_handle (double val, bool notify_toolkit) +{ + graphics_handle h = val; + + base_graphics_object *bgo = new figure (h, 0); + graphics_object go (bgo); + + m_handle_map[h] = go; + + // Notify graphics toolkit. + if (notify_toolkit) + go.initialize (); + + go.override_defaults (); + + return h; +} + +void +gh_manager::push_figure (const graphics_handle& h) +{ + pop_figure (h); + + m_figure_list.push_front (h); +} + +void +gh_manager::pop_figure (const graphics_handle& h) +{ + for (auto it = m_figure_list.begin (); it != m_figure_list.end (); it++) + { + if (*it == h) + { + m_figure_list.erase (it); + break; + } + } +} + +static void +xset_gcbo (const graphics_handle& h) +{ + gh_manager& gh_mgr = octave::__get_gh_manager__ (); + + graphics_object go = gh_mgr.get_object (0); + + root_figure::properties& props + = dynamic_cast<root_figure::properties&> (go.get_properties ()); + + props.set_callbackobject (h.as_octave_value ()); +} + +void +gh_manager::restore_gcbo (void) +{ + octave::autolock guard (m_graphics_lock); + + m_callback_objects.pop_front (); + + xset_gcbo (m_callback_objects.empty () + ? graphics_handle () : m_callback_objects.front ().get_handle ()); +} + +void +gh_manager::execute_listener (const graphics_handle& h, const octave_value& l) +{ + if (octave::thread::is_thread ()) + execute_callback (h, l, octave_value ()); + else + { + octave::autolock guard (m_graphics_lock); + + post_event (graphics_event::create_callback_event (h, l)); + } +} + +void +gh_manager::execute_callback (const graphics_handle& h, + const octave_value& cb_arg, + const octave_value& data) +{ + if (cb_arg.is_defined () && ! cb_arg.isempty ()) + { + octave_value_list args; + octave_value ov_fcn; + octave_function *fcn = nullptr; + + args(0) = h.as_octave_value (); + if (data.is_defined ()) + args(1) = data; + else + args(1) = Matrix (); + + octave::unwind_action_safe restore_gcbo_action + (&gh_manager::restore_gcbo, this); + + graphics_object go (get_object (h)); + if (go) + { + // FIXME: Is the lock necessary when we're only calling a + // const "get" method? + octave::autolock guard (m_graphics_lock); + m_callback_objects.push_front (go); + xset_gcbo (h); + } + + // Copy CB because "function_value" method is non-const. + octave_value cb = cb_arg; + + if (cb.is_function ()) + fcn = cb.function_value (); + else if (cb.is_function_handle ()) + ov_fcn = cb; + else if (cb.is_string ()) + { + int status; + std::string s = cb.string_value (); + + try + { + m_interpreter.eval_string (s, false, status, 0); + } + catch (const octave::execution_exception& ee) + { + m_interpreter.handle_exception (ee); + } + } + else if (cb.iscell () && cb.length () > 0 + && (cb.rows () == 1 || cb.columns () == 1) + && (cb.cell_value ()(0).is_function () + || cb.cell_value ()(0).is_function_handle ())) + { + Cell c = cb.cell_value (); + + ov_fcn = c(0); + + for (int i = 1; i < c.numel () ; i++) + args(1+i) = c(i); + } + else + { + std::string nm = cb.class_name (); + error ("trying to execute non-executable object (class = %s)", + nm.c_str ()); + } + + if (fcn || ov_fcn.is_defined ()) + try + { + if (ov_fcn.is_defined ()) + octave::feval (ov_fcn, args); + else + octave::feval (fcn, args); + } + catch (const octave::execution_exception& ee) + { + m_interpreter.handle_exception (ee); + } + + // Redraw after interacting with a user-interface (ui*) object. + if (Vdrawnow_requested) + { + if (go) + { + std::string go_name + = go.get_properties ().graphics_object_name (); + + if (go_name.length () > 1 + && go_name[0] == 'u' && go_name[1] == 'i') + { + Fdrawnow (m_interpreter); + Vdrawnow_requested = false; + } + } + } + } +} + +static int +process_graphics_events (void) +{ + gh_manager& gh_mgr = octave::__get_gh_manager__ (); + + return gh_mgr.process_events (); +} + +void +gh_manager::post_event (const graphics_event& e) +{ + m_event_queue.push_back (e); + + octave::command_editor::add_event_hook (process_graphics_events); +} + +void +gh_manager::post_callback (const graphics_handle& h, const std::string& name, + const octave_value& data) +{ + octave::autolock guard (m_graphics_lock); + + graphics_object go = get_object (h); + + if (go.valid_object ()) + { + caseless_str cname (name); + int busyaction = base_graphics_event::QUEUE; + + if (cname == "deletefcn" || cname == "createfcn" + || cname == "closerequestfcn" + || ((go.isa ("figure") || go.isa ("uipanel") + || go.isa ("uibuttongroup")) + && (cname == "resizefcn" || cname == "sizechangedfcn"))) + busyaction = base_graphics_event::INTERRUPT; + else if (go.get_properties ().get_busyaction () == "cancel") + busyaction = base_graphics_event::CANCEL; + + // The "closerequestfcn" callback must be executed once the figure has + // been made current. Let "close" do the job. + if (cname == "closerequestfcn") + { + std::string cmd ("close (gcbf ());"); + post_event (graphics_event::create_mcode_event (h, cmd, busyaction)); + } + else + post_event (graphics_event::create_callback_event (h, name, data, + busyaction)); + } +} + +void +gh_manager::post_function (graphics_event::event_fcn fcn, void *fcn_data) +{ + octave::autolock guard (m_graphics_lock); + + post_event (graphics_event::create_function_event (fcn, fcn_data)); +} + +void +gh_manager::post_set (const graphics_handle& h, const std::string& name, + const octave_value& value, bool notify_toolkit, + bool redraw_figure) +{ + octave::autolock guard (m_graphics_lock); + + post_event (graphics_event::create_set_event (h, name, value, notify_toolkit, + redraw_figure)); +} + +int +gh_manager::process_events (bool force) +{ + graphics_event e; + bool old_Vdrawnow_requested = Vdrawnow_requested; + bool events_executed = false; + + do + { + e = graphics_event (); + + { + octave::autolock guard (m_graphics_lock); + + if (! m_event_queue.empty ()) + { + if (m_callback_objects.empty () || force) + { + e = m_event_queue.front (); + + m_event_queue.pop_front (); + } + else + { + const graphics_object& go = m_callback_objects.front (); + + if (go.get_properties ().is_interruptible ()) + { + e = m_event_queue.front (); + + m_event_queue.pop_front (); + } + else + { + std::list<graphics_event>::iterator p = m_event_queue.begin (); + + while (p != m_event_queue.end ()) + if (p->get_busyaction () == base_graphics_event::CANCEL) + { + p = m_event_queue.erase (p); + } + else if (p->get_busyaction () + == base_graphics_event::INTERRUPT) + { + e = (*p); + m_event_queue.erase (p); + break; + } + else + p++; + } + } + } + } + + if (e.ok ()) + { + e.execute (); + events_executed = true; + } + } + while (e.ok ()); + + { + octave::autolock guard (m_graphics_lock); + + if (m_event_queue.empty () && m_event_processing == 0) + octave::command_editor::remove_event_hook (process_graphics_events); + } + + if (events_executed) + octave::flush_stdout (); + + if (Vdrawnow_requested && ! old_Vdrawnow_requested) + { + Fdrawnow (m_interpreter); + + Vdrawnow_requested = false; + } + + return 0; +} + + +/* +## Test interruptible/busyaction properties +%!function cb (h, ~) +%! setappdata (gcbf (), "cb_exec", [getappdata(gcbf (), "cb_exec") h]); +%! drawnow (); +%! setappdata (gcbf (), "cb_exec", [getappdata(gcbf (), "cb_exec") h]); +%!endfunction +%! +%!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ())) +%! hf = figure ("visible", "off", "resizefcn", @cb); +%! graphics_toolkit (hf, "qt"); +%! unwind_protect +%! ## Default +%! hui1 = uicontrol ("parent", hf, "interruptible", "on", "callback", @cb); +%! hui2 = uicontrol ("parent", hf, "busyaction", "queue", "callback", @cb); +%! hui3 = uicontrol ("parent", hf, "busyaction", "queue", "callback", @cb); +%! __go_post_callback__ (hui1, "callback"); +%! __go_post_callback__ (hui2, "callback"); +%! __go_post_callback__ (hui3, "callback"); +%! +%! assert (getappdata (hf, "cb_exec"), []); +%! drawnow (); +%! assert (getappdata (hf, "cb_exec"), [hui1 hui2 hui3 hui3 hui2 hui1]); +%! +%! ## Interruptible off +%! setappdata (hf, "cb_exec", []); +%! set (hui1, "interruptible", "off"); +%! __go_post_callback__ (hui1, "callback"); +%! __go_post_callback__ (hui2, "callback"); +%! __go_post_callback__ (hui3, "callback"); +%! drawnow (); +%! assert (getappdata (hf, "cb_exec"), [hui1 hui1 hui2 hui3 hui3 hui2]); +%! +%! ## "resizefcn" callback interrupts regardless of interruptible property +%! setappdata (hf, "cb_exec", []); +%! __go_post_callback__ (hui1, "callback"); +%! __go_post_callback__ (hf, "resizefcn"); +%! drawnow (); +%! assert (getappdata (hf, "cb_exec"), [hui1 hf hf hui1]); +%! +%! ## test "busyaction" "cancel" +%! setappdata (hf, "cb_exec", []); +%! set (hui2, "busyaction", "cancel"); +%! __go_post_callback__ (hui1, "callback"); +%! __go_post_callback__ (hui2, "callback"); +%! __go_post_callback__ (hui3, "callback"); +%! __go_post_callback__ (hf, "resizefcn"); +%! drawnow (); +%! assert (getappdata (hf, "cb_exec"), [hui1 hf hui3 hui3 hf hui1]); +%! unwind_protect_cleanup +%! close (hf) +%! end_unwind_protect +*/ + +void +gh_manager::enable_event_processing (bool enable) +{ + octave::autolock guard (m_graphics_lock); + + if (enable) + { + m_event_processing++; + + octave::command_editor::add_event_hook (process_graphics_events); + } + else + { + m_event_processing--; + + if (m_event_queue.empty () && m_event_processing == 0) + octave::command_editor::remove_event_hook (process_graphics_events); + } +} + +OCTAVE_END_NAMESPACE(octave)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/gh-manager.h Sat Dec 10 00:30:20 2022 -0500 @@ -0,0 +1,288 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2007-2022 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// Octave is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Octave is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +#if ! defined (octave_gh_manager_h) +#define octave_gh_manager_h 1 + +#include "octave-config.h" + +#include "graphics.h" +#include "gtk-manager.h" + +OCTAVE_BEGIN_NAMESPACE(octave) + +class OCTINTERP_API gh_manager +{ +public: + + typedef std::pair<uint8NDArray /*pixels*/, std::string /*svg*/> latex_data; + + OCTINTERP_API gh_manager (octave::interpreter& interp); + + // FIXME: eventually eliminate these static functions and access + // gh_manager object through the interpreter. + + OCTINTERP_API graphics_handle get_handle (bool integer_figure_handle); + + OCTINTERP_API void free (const graphics_handle& h, bool from_root = false); + + OCTINTERP_API void renumber_figure (const graphics_handle& old_gh, + const graphics_handle& new_gh); + + graphics_handle lookup (double val) const + { + const_iterator p = (octave::math::isnan (val) + ? m_handle_map.end () : m_handle_map.find (val)); + + return (p != m_handle_map.end ()) ? p->first : graphics_handle (); + } + + graphics_handle lookup (const octave_value& val) const + { + return (val.is_real_scalar () + ? lookup (val.double_value ()) : graphics_handle ()); + } + + graphics_object get_object (double val) const + { + return get_object (lookup (val)); + } + + graphics_object get_object (const graphics_handle& h) const + { + const_iterator p = (h.ok () ? m_handle_map.find (h) : m_handle_map.end ()); + + return (p != m_handle_map.end ()) ? p->second : graphics_object (); + } + + OCTINTERP_API graphics_handle + make_graphics_handle (const std::string& go_name, + const graphics_handle& p, + bool integer_figure_handle = false, + bool call_createfcn = true, + bool notify_toolkit = true); + + OCTINTERP_API graphics_handle + make_figure_handle (double val, bool notify_toolkit = true); + + OCTINTERP_API void push_figure (const graphics_handle& h); + + OCTINTERP_API void pop_figure (const graphics_handle& h); + + graphics_handle current_figure (void) const + { + graphics_handle retval; + + for (const auto& hfig : m_figure_list) + { + if (is_handle_visible (hfig)) + retval = hfig; + } + + return retval; + } + + Matrix handle_list (bool show_hidden = false) + { + Matrix retval (1, m_handle_map.size ()); + + octave_idx_type i = 0; + for (const auto& h_iter : m_handle_map) + { + graphics_handle h = h_iter.first; + + if (show_hidden || is_handle_visible (h)) + retval(i++) = h.value (); + } + + retval.resize (1, i); + + return retval; + } + + void lock (void) { m_graphics_lock.lock (); } + + bool try_lock (void) { return m_graphics_lock.try_lock (); } + + void unlock (void) { m_graphics_lock.unlock (); } + + Matrix figure_handle_list (bool show_hidden = false) + { + Matrix retval (1, m_figure_list.size ()); + + octave_idx_type i = 0; + for (const auto& hfig : m_figure_list) + { + if (show_hidden || is_handle_visible (hfig)) + retval(i++) = hfig.value (); + } + + retval.resize (1, i); + + return retval; + } + + OCTINTERP_API void + execute_listener (const graphics_handle& h, const octave_value& l); + + void execute_callback (const graphics_handle& h, + const std::string& name, + const octave_value& data = Matrix ()) + { + octave_value cb; + + if (true) + { + octave::autolock guard (graphics_lock ()); + + graphics_object go = get_object (h); + + if (go.valid_object ()) + cb = go.get (name); + } + + execute_callback (h, cb, data); + } + + OCTINTERP_API void + execute_callback (const graphics_handle& h, const octave_value& cb, + const octave_value& data = Matrix ()); + + OCTINTERP_API void + post_callback (const graphics_handle& h, const std::string& name, + const octave_value& data = Matrix ()); + + OCTINTERP_API void + post_function (graphics_event::event_fcn fcn, void *fcn_data = nullptr); + + OCTINTERP_API void + post_set (const graphics_handle& h, const std::string& name, + const octave_value& value, bool notify_toolkit = true, + bool redraw_figure = false); + + OCTINTERP_API int process_events (bool force = false); + + OCTINTERP_API void enable_event_processing (bool enable = true); + + bool is_handle_visible (const graphics_handle& h) const + { + bool retval = false; + + graphics_object go = get_object (h); + + if (go.valid_object ()) + retval = go.is_handle_visible (); + + return retval; + } + + OCTINTERP_API void close_all_figures (void); + + OCTINTERP_API void restore_gcbo (void); + + OCTINTERP_API void post_event (const graphics_event& e); + + octave::mutex graphics_lock (void) + { + return m_graphics_lock; + } + + latex_data get_latex_data (const std::string& key) const + { + latex_data retval; + + const auto it = m_latex_cache.find (key); + + if (it != m_latex_cache.end ()) + retval = it->second; + + return retval; + } + + void set_latex_data (const std::string& key, latex_data val) + { + // Limit the number of cache entries to 500 + if (m_latex_keys.size () >= 500) + { + auto it = m_latex_cache.find (m_latex_keys.front ()); + + if (it != m_latex_cache.end ()) + m_latex_cache.erase (it); + + m_latex_keys.pop_front (); + } + + m_latex_cache[key] = val; + m_latex_keys.push_back (key); + } + +private: + + typedef std::map<graphics_handle, graphics_object>::iterator iterator; + typedef std::map<graphics_handle, graphics_object>::const_iterator + const_iterator; + + typedef std::set<graphics_handle>::iterator free_list_iterator; + typedef std::set<graphics_handle>::const_iterator const_free_list_iterator; + + typedef std::list<graphics_handle>::iterator figure_list_iterator; + typedef std::list<graphics_handle>::const_iterator const_figure_list_iterator; + + octave::interpreter& m_interpreter; + + // A map of handles to graphics objects. + std::map<graphics_handle, graphics_object> m_handle_map; + + // The available graphics handles. + std::set<graphics_handle> m_handle_free_list; + + // The next handle available if m_handle_free_list is empty. + double m_next_handle; + + // The allocated figure handles. Top of the stack is most recently + // created. + std::list<graphics_handle> m_figure_list; + + // The lock for accessing the graphics sytsem. + octave::mutex m_graphics_lock; + + // The list of events queued by graphics toolkits. + std::list<graphics_event> m_event_queue; + + // The stack of callback objects. + std::list<graphics_object> m_callback_objects; + + // A flag telling whether event processing must be constantly on. + int m_event_processing; + + // Cache of already parsed latex strings. Store a separate list of keys + // to allow for erasing oldest entries if cache size becomes too large. + std::unordered_map<std::string, latex_data> m_latex_cache; + std::list<std::string> m_latex_keys; +}; + +OCTAVE_END_NAMESPACE(octave) + +#endif
--- a/libinterp/corefcn/gl-render.cc Sat Dec 10 00:11:20 2022 -0500 +++ b/libinterp/corefcn/gl-render.cc Sat Dec 10 00:30:20 2022 -0500 @@ -40,6 +40,7 @@ #include "oct-locbuf.h" #include "errwarn.h" +#include "gh-manager.h" #include "gl-render.h" #include "interpreter-private.h" #include "oct-opengl.h"
--- a/libinterp/corefcn/gl2ps-print.cc Sat Dec 10 00:11:20 2022 -0500 +++ b/libinterp/corefcn/gl2ps-print.cc Sat Dec 10 00:30:20 2022 -0500 @@ -47,6 +47,7 @@ #include "unistr-wrappers.h" #include "unwind-prot.h" +#include "gh-manager.h" #include "gl-render.h" #include "interpreter-private.h" #include "oct-opengl.h"
--- a/libinterp/corefcn/graphics-toolkit.cc Sat Dec 10 00:11:20 2022 -0500 +++ b/libinterp/corefcn/graphics-toolkit.cc Sat Dec 10 00:30:20 2022 -0500 @@ -27,6 +27,7 @@ # include "config.h" #endif +#include "gh-manager.h" #include "graphics.h" #include "gtk-manager.h" #include "interpreter-private.h"
--- a/libinterp/corefcn/graphics-utils.cc Sat Dec 10 00:11:20 2022 -0500 +++ b/libinterp/corefcn/graphics-utils.cc Sat Dec 10 00:30:20 2022 -0500 @@ -29,6 +29,7 @@ #include "caseless-str.h" +#include "gh-manager.h" #include "graphics-utils.h" #include "graphics.h" #include "input.h"
--- a/libinterp/corefcn/graphics.cc Sat Dec 10 00:11:20 2022 -0500 +++ b/libinterp/corefcn/graphics.cc Sat Dec 10 00:30:20 2022 -0500 @@ -51,6 +51,7 @@ #include "defun.h" #include "display.h" #include "error.h" +#include "gh-manager.h" #include "graphics-utils.h" #include "graphics.h" #include "input.h" @@ -1200,10 +1201,10 @@ return result; } -static base_graphics_object * +base_graphics_object * make_graphics_object_from_type (const caseless_str& type, - const graphics_handle& h = graphics_handle (), - const graphics_handle& p = graphics_handle ()) + const graphics_handle& h, + const graphics_handle& p) { base_graphics_object *go = nullptr; @@ -2816,147 +2817,6 @@ %! end_unwind_protect */ -static double -make_handle_fraction (void) -{ - static double maxrand = RAND_MAX + 2.0; - - return (rand () + 1.0) / maxrand; -} - -graphics_handle -gh_manager::get_handle (bool integer_figure_handle) -{ - graphics_handle retval; - - if (integer_figure_handle) - { - // Figure handles are positive integers corresponding - // to the figure number. - - // We always want the lowest unused figure number. - - retval = 1; - - while (m_handle_map.find (retval) != m_handle_map.end ()) - retval++; - } - else - { - // Other graphics handles are negative integers plus some random - // fractional part. To avoid running out of integers, we recycle the - // integer part but tack on a new random part each time. - - auto p = m_handle_free_list.begin (); - - if (p != m_handle_free_list.end ()) - { - retval = *p; - m_handle_free_list.erase (p); - } - else - { - retval = graphics_handle (m_next_handle); - - m_next_handle = std::ceil (m_next_handle) - 1.0 - make_handle_fraction (); - } - } - - return retval; -} - -void -gh_manager::free (const graphics_handle& h, bool from_root) -{ - if (h.ok ()) - { - if (h.value () == 0) - error ("graphics_handle::free: can't delete root object"); - - auto p = m_handle_map.find (h); - - if (p == m_handle_map.end ()) - error ("graphics_handle::free: invalid object %g", h.value ()); - - base_properties& bp = p->second.get_properties (); - - if (! p->second.valid_object () || bp.is_beingdeleted ()) - return; - - graphics_handle parent_h = p->second.get_parent (); - graphics_object parent_go = nullptr; - if (! from_root || isfigure (h.value ())) - parent_go = get_object (parent_h); - - bp.set_beingdeleted (true); - - // delete listeners before invalidating object - p->second.remove_all_listeners (); - - bp.delete_children (true, from_root); - - // NOTE: Call the delete function while the object's state is still valid. - octave_value val = bp.get_deletefcn (); - - bp.execute_deletefcn (); - - // Notify graphics toolkit. - p->second.finalize (); - - - // NOTE: Call remove_child before erasing the go from the map if not - // removing from groot. - // A callback function might have already deleted the parent - if ((! from_root || isfigure (h.value ())) && parent_go.valid_object () - && h.ok ()) - parent_go.remove_child (h); - - // Note: this will be valid only for first explicitly deleted - // object. All its children will then have an - // unknown graphics toolkit. - - // Graphics handles for non-figure objects are negative - // integers plus some random fractional part. To avoid - // running out of integers, we recycle the integer part - // but tack on a new random part each time. - - m_handle_map.erase (p); - - if (h.value () < 0) - m_handle_free_list.insert - (std::ceil (h.value ()) - make_handle_fraction ()); - } -} - -void -gh_manager::renumber_figure (const graphics_handle& old_gh, - const graphics_handle& new_gh) -{ - auto p = m_handle_map.find (old_gh); - - if (p == m_handle_map.end ()) - error ("graphics_handle::free: invalid object %g", old_gh.value ()); - - graphics_object go = p->second; - - m_handle_map.erase (p); - - m_handle_map[new_gh] = go; - - if (old_gh.value () < 0) - m_handle_free_list.insert (std::ceil (old_gh.value ()) - - make_handle_fraction ()); - - for (auto& hfig : m_figure_list) - { - if (hfig == old_gh) - { - hfig = new_gh; - break; - } - } -} - // This function is NOT equivalent to the scripting language function gcf. graphics_handle gcf (void) @@ -2977,50 +2837,6 @@ : val.double_value (); } -void -gh_manager::close_all_figures (void) -{ - // FIXME: should we process or discard pending events? - - m_event_queue.clear (); - - // Don't use m_figure_list_iterator because we'll be removing elements - // from the list elsewhere. - - Matrix hlist = figure_handle_list (true); - - for (octave_idx_type i = 0; i < hlist.numel (); i++) - { - graphics_handle h = lookup (hlist(i)); - - if (h.ok ()) - close_figure (h); - } - - // They should all be closed now. If not, force them to close. - - hlist = figure_handle_list (true); - - for (octave_idx_type i = 0; i < hlist.numel (); i++) - { - graphics_handle h = lookup (hlist(i)); - - if (h.ok ()) - force_close_figure (h); - } - - // None left now, right? - - hlist = figure_handle_list (true); - - if (hlist.numel () != 0) - warning ("gh_manager::close_all_figures: some graphics elements failed to close"); - - // Clear all callback objects from our list. - - m_callback_objects.clear (); -} - static void adopt (const graphics_handle& parent_h, const graphics_handle& h) { @@ -11753,118 +11569,6 @@ return parent_go.get_factory_default (type () + name); } -// We use a random value for the handle to avoid issues with plots and -// scalar values for the first argument. -gh_manager::gh_manager (octave::interpreter& interp) - : m_interpreter (interp), m_handle_map (), m_handle_free_list (), - m_next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)), - m_figure_list (), m_graphics_lock (), m_event_queue (), - m_callback_objects (), m_event_processing (0) -{ - m_handle_map[0] = graphics_object (new root_figure ()); - - octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ (); - - // Make sure the default graphics toolkit is registered. - gtk_mgr.default_toolkit (); -} - -graphics_handle -gh_manager::make_graphics_handle (const std::string& go_name, - const graphics_handle& p, - bool integer_figure_handle, - bool call_createfcn, bool notify_toolkit) -{ - graphics_handle h = get_handle (integer_figure_handle); - - base_graphics_object *bgo = make_graphics_object_from_type (go_name, h, p); - - if (! bgo) - error ("gh_manager::make_graphics_handle: invalid object type '%s'", - go_name.c_str ()); - - graphics_object go (bgo); - - m_handle_map[h] = go; - - if (go_name == "axes") - { - // Handle defaults for labels since overriding defaults for - // them can't work before the axes object is fully - // constructed. - - axes::properties& props - = dynamic_cast<axes::properties&> (go.get_properties ()); - - graphics_object tgo; - - tgo = get_object (props.get_xlabel ()); - tgo.override_defaults (); - - tgo = get_object (props.get_ylabel ()); - tgo.override_defaults (); - - tgo = get_object (props.get_zlabel ()); - tgo.override_defaults (); - - tgo = get_object (props.get_title ()); - tgo.override_defaults (); - } - - // Overriding defaults will work now because the handle is valid - // and we can find parent objects (not just handles). - go.override_defaults (); - - if (call_createfcn) - bgo->get_properties ().execute_createfcn (); - - // Notify graphics toolkit. - if (notify_toolkit) - go.initialize (); - - return h; -} - -graphics_handle -gh_manager::make_figure_handle (double val, bool notify_toolkit) -{ - graphics_handle h = val; - - base_graphics_object *bgo = new figure (h, 0); - graphics_object go (bgo); - - m_handle_map[h] = go; - - // Notify graphics toolkit. - if (notify_toolkit) - go.initialize (); - - go.override_defaults (); - - return h; -} - -void -gh_manager::push_figure (const graphics_handle& h) -{ - pop_figure (h); - - m_figure_list.push_front (h); -} - -void -gh_manager::pop_figure (const graphics_handle& h) -{ - for (auto it = m_figure_list.begin (); it != m_figure_list.end (); it++) - { - if (*it == h) - { - m_figure_list.erase (it); - break; - } - } -} - class callback_event : public base_graphics_event { @@ -12089,373 +11793,6 @@ redraw_figure)); } -static void -xset_gcbo (const graphics_handle& h) -{ - gh_manager& gh_mgr = octave::__get_gh_manager__ (); - - graphics_object go = gh_mgr.get_object (0); - - root_figure::properties& props - = dynamic_cast<root_figure::properties&> (go.get_properties ()); - - props.set_callbackobject (h.as_octave_value ()); -} - -void -gh_manager::restore_gcbo (void) -{ - octave::autolock guard (m_graphics_lock); - - m_callback_objects.pop_front (); - - xset_gcbo (m_callback_objects.empty () - ? graphics_handle () : m_callback_objects.front ().get_handle ()); -} - -void -gh_manager::execute_listener (const graphics_handle& h, const octave_value& l) -{ - if (octave::thread::is_thread ()) - execute_callback (h, l, octave_value ()); - else - { - octave::autolock guard (m_graphics_lock); - - post_event (graphics_event::create_callback_event (h, l)); - } -} - -void -gh_manager::execute_callback (const graphics_handle& h, - const octave_value& cb_arg, - const octave_value& data) -{ - if (cb_arg.is_defined () && ! cb_arg.isempty ()) - { - octave_value_list args; - octave_value ov_fcn; - octave_function *fcn = nullptr; - - args(0) = h.as_octave_value (); - if (data.is_defined ()) - args(1) = data; - else - args(1) = Matrix (); - - octave::unwind_action_safe restore_gcbo_action - (&gh_manager::restore_gcbo, this); - - graphics_object go (get_object (h)); - if (go) - { - // FIXME: Is the lock necessary when we're only calling a - // const "get" method? - octave::autolock guard (m_graphics_lock); - m_callback_objects.push_front (go); - xset_gcbo (h); - } - - // Copy CB because "function_value" method is non-const. - octave_value cb = cb_arg; - - if (cb.is_function ()) - fcn = cb.function_value (); - else if (cb.is_function_handle ()) - ov_fcn = cb; - else if (cb.is_string ()) - { - int status; - std::string s = cb.string_value (); - - try - { - m_interpreter.eval_string (s, false, status, 0); - } - catch (const octave::execution_exception& ee) - { - m_interpreter.handle_exception (ee); - } - } - else if (cb.iscell () && cb.length () > 0 - && (cb.rows () == 1 || cb.columns () == 1) - && (cb.cell_value ()(0).is_function () - || cb.cell_value ()(0).is_function_handle ())) - { - Cell c = cb.cell_value (); - - ov_fcn = c(0); - - for (int i = 1; i < c.numel () ; i++) - args(1+i) = c(i); - } - else - { - std::string nm = cb.class_name (); - error ("trying to execute non-executable object (class = %s)", - nm.c_str ()); - } - - if (fcn || ov_fcn.is_defined ()) - try - { - if (ov_fcn.is_defined ()) - octave::feval (ov_fcn, args); - else - octave::feval (fcn, args); - } - catch (const octave::execution_exception& ee) - { - m_interpreter.handle_exception (ee); - } - - // Redraw after interacting with a user-interface (ui*) object. - if (Vdrawnow_requested) - { - if (go) - { - std::string go_name - = go.get_properties ().graphics_object_name (); - - if (go_name.length () > 1 - && go_name[0] == 'u' && go_name[1] == 'i') - { - Fdrawnow (m_interpreter); - Vdrawnow_requested = false; - } - } - } - } -} - -static int -process_graphics_events (void) -{ - gh_manager& gh_mgr = octave::__get_gh_manager__ (); - - return gh_mgr.process_events (); -} - -void -gh_manager::post_event (const graphics_event& e) -{ - m_event_queue.push_back (e); - - octave::command_editor::add_event_hook (process_graphics_events); -} - -void -gh_manager::post_callback (const graphics_handle& h, const std::string& name, - const octave_value& data) -{ - octave::autolock guard (m_graphics_lock); - - graphics_object go = get_object (h); - - if (go.valid_object ()) - { - caseless_str cname (name); - int busyaction = base_graphics_event::QUEUE; - - if (cname == "deletefcn" || cname == "createfcn" - || cname == "closerequestfcn" - || ((go.isa ("figure") || go.isa ("uipanel") - || go.isa ("uibuttongroup")) - && (cname == "resizefcn" || cname == "sizechangedfcn"))) - busyaction = base_graphics_event::INTERRUPT; - else if (go.get_properties ().get_busyaction () == "cancel") - busyaction = base_graphics_event::CANCEL; - - // The "closerequestfcn" callback must be executed once the figure has - // been made current. Let "close" do the job. - if (cname == "closerequestfcn") - { - std::string cmd ("close (gcbf ());"); - post_event (graphics_event::create_mcode_event (h, cmd, busyaction)); - } - else - post_event (graphics_event::create_callback_event (h, name, data, - busyaction)); - } -} - -void -gh_manager::post_function (graphics_event::event_fcn fcn, void *fcn_data) -{ - octave::autolock guard (m_graphics_lock); - - post_event (graphics_event::create_function_event (fcn, fcn_data)); -} - -void -gh_manager::post_set (const graphics_handle& h, const std::string& name, - const octave_value& value, bool notify_toolkit, - bool redraw_figure) -{ - octave::autolock guard (m_graphics_lock); - - post_event (graphics_event::create_set_event (h, name, value, notify_toolkit, - redraw_figure)); -} - -int -gh_manager::process_events (bool force) -{ - graphics_event e; - bool old_Vdrawnow_requested = Vdrawnow_requested; - bool events_executed = false; - - do - { - e = graphics_event (); - - { - octave::autolock guard (m_graphics_lock); - - if (! m_event_queue.empty ()) - { - if (m_callback_objects.empty () || force) - { - e = m_event_queue.front (); - - m_event_queue.pop_front (); - } - else - { - const graphics_object& go = m_callback_objects.front (); - - if (go.get_properties ().is_interruptible ()) - { - e = m_event_queue.front (); - - m_event_queue.pop_front (); - } - else - { - std::list<graphics_event>::iterator p = m_event_queue.begin (); - - while (p != m_event_queue.end ()) - if (p->get_busyaction () == base_graphics_event::CANCEL) - { - p = m_event_queue.erase (p); - } - else if (p->get_busyaction () - == base_graphics_event::INTERRUPT) - { - e = (*p); - m_event_queue.erase (p); - break; - } - else - p++; - } - } - } - } - - if (e.ok ()) - { - e.execute (); - events_executed = true; - } - } - while (e.ok ()); - - { - octave::autolock guard (m_graphics_lock); - - if (m_event_queue.empty () && m_event_processing == 0) - octave::command_editor::remove_event_hook (process_graphics_events); - } - - if (events_executed) - octave::flush_stdout (); - - if (Vdrawnow_requested && ! old_Vdrawnow_requested) - { - Fdrawnow (m_interpreter); - - Vdrawnow_requested = false; - } - - return 0; -} - - -/* -## Test interruptible/busyaction properties -%!function cb (h, ~) -%! setappdata (gcbf (), "cb_exec", [getappdata(gcbf (), "cb_exec") h]); -%! drawnow (); -%! setappdata (gcbf (), "cb_exec", [getappdata(gcbf (), "cb_exec") h]); -%!endfunction -%! -%!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ())) -%! hf = figure ("visible", "off", "resizefcn", @cb); -%! graphics_toolkit (hf, "qt"); -%! unwind_protect -%! ## Default -%! hui1 = uicontrol ("parent", hf, "interruptible", "on", "callback", @cb); -%! hui2 = uicontrol ("parent", hf, "busyaction", "queue", "callback", @cb); -%! hui3 = uicontrol ("parent", hf, "busyaction", "queue", "callback", @cb); -%! __go_post_callback__ (hui1, "callback"); -%! __go_post_callback__ (hui2, "callback"); -%! __go_post_callback__ (hui3, "callback"); -%! -%! assert (getappdata (hf, "cb_exec"), []); -%! drawnow (); -%! assert (getappdata (hf, "cb_exec"), [hui1 hui2 hui3 hui3 hui2 hui1]); -%! -%! ## Interruptible off -%! setappdata (hf, "cb_exec", []); -%! set (hui1, "interruptible", "off"); -%! __go_post_callback__ (hui1, "callback"); -%! __go_post_callback__ (hui2, "callback"); -%! __go_post_callback__ (hui3, "callback"); -%! drawnow (); -%! assert (getappdata (hf, "cb_exec"), [hui1 hui1 hui2 hui3 hui3 hui2]); -%! -%! ## "resizefcn" callback interrupts regardless of interruptible property -%! setappdata (hf, "cb_exec", []); -%! __go_post_callback__ (hui1, "callback"); -%! __go_post_callback__ (hf, "resizefcn"); -%! drawnow (); -%! assert (getappdata (hf, "cb_exec"), [hui1 hf hf hui1]); -%! -%! ## test "busyaction" "cancel" -%! setappdata (hf, "cb_exec", []); -%! set (hui2, "busyaction", "cancel"); -%! __go_post_callback__ (hui1, "callback"); -%! __go_post_callback__ (hui2, "callback"); -%! __go_post_callback__ (hui3, "callback"); -%! __go_post_callback__ (hf, "resizefcn"); -%! drawnow (); -%! assert (getappdata (hf, "cb_exec"), [hui1 hf hui3 hui3 hf hui1]); -%! unwind_protect_cleanup -%! close (hf) -%! end_unwind_protect -*/ - -void -gh_manager::enable_event_processing (bool enable) -{ - octave::autolock guard (m_graphics_lock); - - if (enable) - { - m_event_processing++; - - octave::command_editor::add_event_hook (process_graphics_events); - } - else - { - m_event_processing--; - - if (m_event_queue.empty () && m_event_processing == 0) - octave::command_editor::remove_event_hook (process_graphics_events); - } -} - property_list::plist_map_type root_figure::init_factory_properties (void) {
--- a/libinterp/corefcn/graphics.in.h Sat Dec 10 00:11:20 2022 -0500 +++ b/libinterp/corefcn/graphics.in.h Sat Dec 10 00:30:20 2022 -0500 @@ -6691,255 +6691,10 @@ std::shared_ptr <base_graphics_event> m_rep; }; -class OCTINTERP_API gh_manager -{ -public: - - typedef std::pair<uint8NDArray /*pixels*/, std::string /*svg*/> latex_data; - - OCTINTERP_API gh_manager (octave::interpreter& interp); - - // FIXME: eventually eliminate these static functions and access - // gh_manager object through the interpreter. - - OCTINTERP_API graphics_handle get_handle (bool integer_figure_handle); - - OCTINTERP_API void free (const graphics_handle& h, bool from_root = false); - - OCTINTERP_API void renumber_figure (const graphics_handle& old_gh, - const graphics_handle& new_gh); - - graphics_handle lookup (double val) const - { - const_iterator p = (octave::math::isnan (val) - ? m_handle_map.end () : m_handle_map.find (val)); - - return (p != m_handle_map.end ()) ? p->first : graphics_handle (); - } - - graphics_handle lookup (const octave_value& val) const - { - return (val.is_real_scalar () - ? lookup (val.double_value ()) : graphics_handle ()); - } - - graphics_object get_object (double val) const - { - return get_object (lookup (val)); - } - - graphics_object get_object (const graphics_handle& h) const - { - const_iterator p = (h.ok () ? m_handle_map.find (h) : m_handle_map.end ()); - - return (p != m_handle_map.end ()) ? p->second : graphics_object (); - } - - OCTINTERP_API graphics_handle - make_graphics_handle (const std::string& go_name, - const graphics_handle& p, - bool integer_figure_handle = false, - bool call_createfcn = true, - bool notify_toolkit = true); - - OCTINTERP_API graphics_handle - make_figure_handle (double val, bool notify_toolkit = true); - - OCTINTERP_API void push_figure (const graphics_handle& h); - - OCTINTERP_API void pop_figure (const graphics_handle& h); - - graphics_handle current_figure (void) const - { - graphics_handle retval; - - for (const auto& hfig : m_figure_list) - { - if (is_handle_visible (hfig)) - retval = hfig; - } - - return retval; - } - - Matrix handle_list (bool show_hidden = false) - { - Matrix retval (1, m_handle_map.size ()); - - octave_idx_type i = 0; - for (const auto& h_iter : m_handle_map) - { - graphics_handle h = h_iter.first; - - if (show_hidden || is_handle_visible (h)) - retval(i++) = h.value (); - } - - retval.resize (1, i); - - return retval; - } - - void lock (void) { m_graphics_lock.lock (); } - - bool try_lock (void) { return m_graphics_lock.try_lock (); } - - void unlock (void) { m_graphics_lock.unlock (); } - - Matrix figure_handle_list (bool show_hidden = false) - { - Matrix retval (1, m_figure_list.size ()); - - octave_idx_type i = 0; - for (const auto& hfig : m_figure_list) - { - if (show_hidden || is_handle_visible (hfig)) - retval(i++) = hfig.value (); - } - - retval.resize (1, i); - - return retval; - } - - OCTINTERP_API void - execute_listener (const graphics_handle& h, const octave_value& l); - - void execute_callback (const graphics_handle& h, - const std::string& name, - const octave_value& data = Matrix ()) - { - octave_value cb; - - if (true) - { - octave::autolock guard (graphics_lock ()); - - graphics_object go = get_object (h); - - if (go.valid_object ()) - cb = go.get (name); - } - - execute_callback (h, cb, data); - } - - OCTINTERP_API void - execute_callback (const graphics_handle& h, const octave_value& cb, - const octave_value& data = Matrix ()); - - OCTINTERP_API void - post_callback (const graphics_handle& h, const std::string& name, - const octave_value& data = Matrix ()); - - OCTINTERP_API void - post_function (graphics_event::event_fcn fcn, void *fcn_data = nullptr); - - OCTINTERP_API void - post_set (const graphics_handle& h, const std::string& name, - const octave_value& value, bool notify_toolkit = true, - bool redraw_figure = false); - - OCTINTERP_API int process_events (bool force = false); - - OCTINTERP_API void enable_event_processing (bool enable = true); - - bool is_handle_visible (const graphics_handle& h) const - { - bool retval = false; - - graphics_object go = get_object (h); - - if (go.valid_object ()) - retval = go.is_handle_visible (); - - return retval; - } - - OCTINTERP_API void close_all_figures (void); - - OCTINTERP_API void restore_gcbo (void); - - OCTINTERP_API void post_event (const graphics_event& e); - - octave::mutex graphics_lock (void) - { - return m_graphics_lock; - } - - latex_data get_latex_data (const std::string& key) const - { - latex_data retval; - - const auto it = m_latex_cache.find (key); - - if (it != m_latex_cache.end ()) - retval = it->second; - - return retval; - } - - void set_latex_data (const std::string& key, latex_data val) - { - // Limit the number of cache entries to 500 - if (m_latex_keys.size () >= 500) - { - auto it = m_latex_cache.find (m_latex_keys.front ()); - - if (it != m_latex_cache.end ()) - m_latex_cache.erase (it); - - m_latex_keys.pop_front (); - } - - m_latex_cache[key] = val; - m_latex_keys.push_back (key); - } - -private: - - typedef std::map<graphics_handle, graphics_object>::iterator iterator; - typedef std::map<graphics_handle, graphics_object>::const_iterator - const_iterator; - - typedef std::set<graphics_handle>::iterator free_list_iterator; - typedef std::set<graphics_handle>::const_iterator const_free_list_iterator; - - typedef std::list<graphics_handle>::iterator figure_list_iterator; - typedef std::list<graphics_handle>::const_iterator const_figure_list_iterator; - - octave::interpreter& m_interpreter; - - // A map of handles to graphics objects. - std::map<graphics_handle, graphics_object> m_handle_map; - - // The available graphics handles. - std::set<graphics_handle> m_handle_free_list; - - // The next handle available if m_handle_free_list is empty. - double m_next_handle; - - // The allocated figure handles. Top of the stack is most recently - // created. - std::list<graphics_handle> m_figure_list; - - // The lock for accessing the graphics sytsem. - octave::mutex m_graphics_lock; - - // The list of events queued by graphics toolkits. - std::list<graphics_event> m_event_queue; - - // The stack of callback objects. - std::list<graphics_object> m_callback_objects; - - // A flag telling whether event processing must be constantly on. - int m_event_processing; - - // Cache of already parsed latex strings. Store a separate list of keys - // to allow for erasing oldest entries if cache size becomes too large. - std::unordered_map<std::string, latex_data> m_latex_cache; - std::list<std::string> m_latex_keys; -}; +OCTINTERP_API base_graphics_object * +make_graphics_object_from_type (const caseless_str& type, + const graphics_handle& h = graphics_handle (), + const graphics_handle& p = graphics_handle ()); OCTINTERP_API void get_children_limits (double& min_val, double& max_val,
--- a/libinterp/corefcn/interpreter.h Sat Dec 10 00:11:20 2022 -0500 +++ b/libinterp/corefcn/interpreter.h Sat Dec 10 00:30:20 2022 -0500 @@ -44,6 +44,7 @@ #include "environment.h" #include "error.h" #include "event-manager.h" +#include "gh-manager.h" #include "graphics.h" #include "gtk-manager.h" #include "help.h"
--- a/libinterp/corefcn/module.mk Sat Dec 10 00:11:20 2022 -0500 +++ b/libinterp/corefcn/module.mk Sat Dec 10 00:30:20 2022 -0500 @@ -38,6 +38,7 @@ %reldir%/fcn-info.h \ %reldir%/file-io.h \ %reldir%/ft-text-renderer.h \ + %reldir%/gh-manager.h \ %reldir%/gl-render.h \ %reldir%/gl2ps-print.h \ %reldir%/graphics-handle.h \ @@ -177,6 +178,7 @@ %reldir%/getgrent.cc \ %reldir%/getpwent.cc \ %reldir%/getrusage.cc \ + %reldir%/gh-manager.cc \ %reldir%/givens.cc \ %reldir%/gl-render.cc \ %reldir%/gl2ps-print.cc \