# HG changeset patch # User Michael Goffioul # Date 1322038765 0 # Node ID 3b654a0753b11196d4bb3063c05ba52ba1a55608 # Parent 7b83576b3b489bd4c322ff95868cc4fe2cd9eec7 Implement waitfor, uiwait and uiresume. * liboctave/cmd-edit.h (command_editor::run_event_hooks): New static method. * liboctave/cmd-edit.cc (command_editor::run_event_hooks): Implement it. * src/graphics.h.in (listener_mode::PREDELETE): New enum value. * src/graphics.cc (, "cutils.h"): New included headers. (base_properties::has_dynamic_properties): Look also into all_props. (gh_manager::do_execute_callback): Allow any type of function to be specified, not only function handles. (waitfor_results): New utility static variable. (compare_property_values, cleanup_waitfor_id, do_cleanup_waitfor_listener, cleanup_waitfor_postset_listener, cleanup_waitfor_predelete_listener, waitfor_listener, waitfor_del_listener): New utility static functions. (Fwaitfor): New function. * plot/uiwait.m: New function. * plot/uiresume.m: Likewise. * plot/modules.mk (plot_FCN_FILES): Add them to the list. diff -r 7b83576b3b48 -r 3b654a0753b1 liboctave/cmd-edit.cc --- a/liboctave/cmd-edit.cc Wed Nov 23 00:20:00 2011 -0500 +++ b/liboctave/cmd-edit.cc Wed Nov 23 08:59:25 2011 +0000 @@ -1227,6 +1227,12 @@ } void +command_editor::run_event_hooks (void) +{ + event_handler (); +} + +void command_editor::read_init_file (const std::string& file_arg) { if (instance_ok ()) diff -r 7b83576b3b48 -r 3b654a0753b1 liboctave/cmd-edit.h --- a/liboctave/cmd-edit.h Wed Nov 23 00:20:00 2011 -0500 +++ b/liboctave/cmd-edit.h Wed Nov 23 08:59:25 2011 +0000 @@ -137,6 +137,8 @@ static void remove_event_hook (event_hook_fcn f); + static void run_event_hooks (void); + static void read_init_file (const std::string& file = std::string ()); static void re_read_init_file (void); diff -r 7b83576b3b48 -r 3b654a0753b1 scripts/plot/module.mk --- a/scripts/plot/module.mk Wed Nov 23 00:20:00 2011 -0500 +++ b/scripts/plot/module.mk Wed Nov 23 08:59:25 2011 +0000 @@ -190,8 +190,10 @@ plot/uipanel.m \ plot/uipushtool.m \ plot/uiputfile.m \ + plot/uiresume.m \ plot/uitoggletool.m \ plot/uitoolbar.m \ + plot/uiwait.m \ plot/view.m \ plot/waitforbuttonpress.m \ plot/whitebg.m \ diff -r 7b83576b3b48 -r 3b654a0753b1 scripts/plot/uiresume.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/plot/uiresume.m Wed Nov 23 08:59:25 2011 +0000 @@ -0,0 +1,45 @@ +## Copyright (C) 2011 Michael Goffioul +## +## 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 +## . + +## -*- texinfo -*- +## @deftypefn {Function File} uiresume (@var{h}) +## Resumes program execution suspended with @code{uiwait}. The handle @var{h} +## must be the same as the on specified in @code{uiwait}. If the handle +## is invalid or there is no @code{uiwait} call pending for the figure +## with handle @var{h}, this function does nothing. +## @seealso{uiwait} +## @end deftypefn + +## Author: goffioul + +function uiresume (h) + + if (! ishandle (h) || ! strcmp (get (h, "type"), "figure")) + error ("uiresume: invalid figure handle"); + endif + + try + uiwait_state = get (h, "__uiwait_state__"); + if (strcmp (uiwait_state, "active")) + set (h, "__uiwait_state__", "triggered"); + endif + catch + # Ignore exception + end_try_catch + +endfunction diff -r 7b83576b3b48 -r 3b654a0753b1 scripts/plot/uiwait.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/plot/uiwait.m Wed Nov 23 08:59:25 2011 +0000 @@ -0,0 +1,80 @@ +## Copyright (C) 2011 Michael Goffioul +## +## 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 +## . + +## -*- texinfo -*- +## @deftypefn {Function File} uiwait +## @deftypefnx {Function File} uiwait (@var{h}) +## @deftypefnx {Function File} uiwait (@var{h}, @var{timeout}) +## Suspends program execution until the figure with handle @var{h} is +## deleted or @code{uiresume} is called. When no figure handle is specified, +## this function uses the current figure. +## +## If the figure handle is invalid or there is no current figure, this +## functions returns immediately. +## +## When specified, @var{timeout} defines the number of seconds to wait +## for the figure deletion or the @code{uiresume} call. The timeout value +## must be at least 1. If a smaller value is specified, a warning is issued +## and a timeout value of 1 is used instead. If a non integer value is +## specified, it is truncated towards 0. If @var{timeout} is not specified, +## the program execution is suspended indefinitely. +## @seealso{uiresume, waitfor} +## @end deftypefn + +## Author: goffioul + +function uiwait (varargin) + + h = []; + timeout = []; + + if (nargin == 0) + h = get (0, "currentfigure"); + else + h = varargin{1}; + if (! ishandle (h) || ! strcmp (get (h, "type"), "figure")) + error ("uiwait: invalid figure handle"); + endif + if (nargin > 1) + timeout = varargin{2}; + endif + endif + + if (! isempty (h)) + unwind_protect + try + addproperty ("__uiwait_state__", h, "radio", "none|{active}|triggered"); + catch + if (! strcmp (get (h, "__uiwait_state__"), "none")) + error ("uiwait: an active uiwait call for this figure already exists"); + endif + set (h, "__uiwait_state__", "active"); + end_try_catch + waitfor_args = {h, "__uiwait_state__", "triggered"}; + if (! isempty (timeout)) + waitfor_args(end+1:end+2) = {"timeout", timeout}; + endif + waitfor (waitfor_args{:}); + unwind_protect_cleanup + if (ishandle (h) && isprop (h, "__uiwait_state__")) + set (h, "__uiwait_state__", "none"); + endif + end_unwind_protect + endif + +endfunction diff -r 7b83576b3b48 -r 3b654a0753b1 src/graphics.cc --- a/src/graphics.cc Wed Nov 23 00:20:00 2011 -0500 +++ b/src/graphics.cc Wed Nov 23 08:59:25 2011 +0000 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,7 @@ #include "file-stat.h" #include "cmd-edit.h" +#include "cutils.h" #include "defun.h" #include "display.h" #include "error.h" @@ -2484,7 +2486,10 @@ { const std::set& dynprops = dynamic_property_names (); - return dynprops.find (pname) != dynprops.end (); + if (dynprops.find (pname) != dynprops.end ()) + return true; + else + return all_props.find (pname) != all_props.end (); } void @@ -7679,7 +7684,7 @@ octave_value cb = cb_arg; - if (cb.is_function_handle ()) + if (cb.is_function () || cb.is_function_handle ()) fcn = cb.function_value (); else if (cb.is_string ()) { @@ -7690,7 +7695,8 @@ } else if (cb.is_cell () && cb.length () > 0 && (cb.rows () == 1 || cb.columns () == 1) - && cb.cell_value ()(0).is_function_handle ()) + && (cb.cell_value ()(0).is_function () + || cb.cell_value ()(0).is_function_handle ())) { Cell c = cb.cell_value (); @@ -9433,3 +9439,396 @@ return ret; } + +static bool +compare_property_values (const octave_value& o1, const octave_value& o2) +{ + octave_value_list args (2); + + args(0) = o1; + args(1) = o2; + + octave_value_list result = feval ("isequal", args, 1); + + if (! error_state && result.length () > 0) + return result(0).bool_value (); + + return false; +} + +static std::map waitfor_results; + +static void +cleanup_waitfor_id (uint32_t id) +{ + waitfor_results.erase (id); +} + +static void +do_cleanup_waitfor_listener (const octave_value& listener, + listener_mode mode = POSTSET) +{ + Cell c = listener.cell_value (); + + if (c.numel () >= 4) + { + double h = c(2).double_value (); + + if (! error_state) + { + caseless_str pname = c(3).string_value (); + + if (! error_state) + { + gh_manager::auto_lock guard; + + graphics_handle handle = gh_manager::lookup (h); + + if (handle.ok ()) + { + graphics_object go = gh_manager::get_object (handle); + + if (go.get_properties ().has_property (pname)) + { + go.get_properties () + .delete_listener (pname, listener, mode); + if (mode == POSTSET) + go.get_properties () + .delete_listener (pname, listener, PERSISTENT); + } + } + } + } + } +} + +static void +cleanup_waitfor_postset_listener(const octave_value& listener) +{ do_cleanup_waitfor_listener (listener, POSTSET); } + +static void +cleanup_waitfor_predelete_listener(const octave_value& listener) +{ do_cleanup_waitfor_listener (listener, PREDELETE); } + +static octave_value_list +waitfor_listener (const octave_value_list& args, int) +{ + if (args.length () > 3) + { + uint32_t id = args(2).uint32_scalar_value ().value (); + + if (! error_state) + { + if (args.length () > 5) + { + double h = args(0).double_value (); + + if (! error_state) + { + caseless_str pname = args(4).string_value (); + + if (! error_state) + { + gh_manager::auto_lock guard; + + graphics_handle handle = gh_manager::lookup (h); + + if (handle.ok ()) + { + graphics_object go = gh_manager::get_object (handle); + octave_value pvalue = go.get (pname); + + if (compare_property_values (pvalue, args(5))) + waitfor_results[id] = true; + } + } + } + } + else + waitfor_results[id] = true; + } + } + + return octave_value_list (); +} + +static octave_value_list +waitfor_del_listener (const octave_value_list& args, int) +{ + if (args.length () > 2) + { + uint32_t id = args(2).uint32_scalar_value ().value (); + + if (! error_state) + waitfor_results[id] = true; + } + + return octave_value_list (); +} + +DEFUN (waitfor, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} waitfor (@var{h})\n\ +@deftypefnx {Built-in Function} {} waitfor (@var{h}, @var{prop})\n\ +@deftypefnx {Built-in Function} {} waitfor (@var{h}, @var{prop}, @var{value})\n\ +@deftypefnx {Built-in Function} {} waitfor (@dots{}, \"timeout\", @var{timeout})\n\ +Suspends the execution of the current program until a condition is\n\ +satisfied on the graphics handle @var{h}. While the program is suspended\n\ +graphics events are still being processed normally, allowing callbacks to\n\ +modify the state of graphics objects. This function is reentrant and can be\n\ +called from a callback, while another @code{waitfor} call is pending at\n\ +top-level.\n\ +\n\ +In the first form, program execution is suspended until the graphics object\n\ +@var{h} is destroyed. If the graphics handle is invalid, the function\n\ +returns immediately.\n\ +\n\ +In the second form, execution is suspended until the graphics object is\n\ +destroyed or the property named @var{prop} is modified. If the graphics\n\ +handle is invalid or the property does not exist, the function returns\n\ +immediately.\n\ +\n\ +In the third form, execution is suspended until the graphics object is\n\ +destroyed or the property named @var{prop} is set to @var{value}. The\n\ +function @code{isequal} is used to compare property values. If the graphics\n\ +handle is invalid, the property does not exist or the property is already\n\ +set to @var{value}, the function returns immediately.\n\ +\n\ +An optional timeout can be specified using the property @code{timeout}.\n\ +This timeout value is the number of seconds to wait for the condition to be\n\ +true. @var{timeout} must be at least 1. If a smaller value is specified, a\n\ +warning is issued and a value of 1 is used instead. If the timeout value is\n\ +not an integer, it is truncated towards 0.\n\ +\n\ +To define a condition on a property named @code{timeout}, use the string\n\ +@code{\\timeout} instead.\n\ +\n\ +In all cases, typing CTRL-C stops program execution immediately.\n\ +@seealso{isequal}\n\ +@end deftypefn") +{ + if (args.length () > 0) + { + double h = args(0).double_value (); + + if (! error_state) + { + caseless_str pname; + + unwind_protect frame; + + static uint32_t id_counter = 0; + uint32_t id = 0; + + int max_arg_index = 0; + int timeout_index = -1; + + int timeout = 0; + + if (args.length () > 1) + { + pname = args(1).string_value (); + if (! error_state + && ! pname.empty () + && ! pname.compare ("timeout")) + { + if (pname.compare ("\\timeout")) + pname = "timeout"; + + static octave_value wf_listener; + + if (! wf_listener.is_defined ()) + wf_listener = + octave_value (new octave_builtin (waitfor_listener, + "waitfor_listener")); + + max_arg_index++; + if (args.length () > 2) + { + if (args(2).is_string ()) + { + caseless_str s = args(2).string_value (); + + if (! error_state) + { + if (s.compare ("timeout")) + timeout_index = 2; + else + max_arg_index++; + } + } + else + max_arg_index++; + } + + Cell listener (1, max_arg_index >= 2 ? 5 : 4); + + id = id_counter++; + frame.add_fcn (cleanup_waitfor_id, id); + waitfor_results[id] = false; + + listener(0) = wf_listener; + listener(1) = octave_uint32 (id); + listener(2) = h; + listener(3) = pname; + + if (max_arg_index >= 2) + listener(4) = args(2); + + octave_value ov_listener (listener); + + gh_manager::auto_lock guard; + + graphics_handle handle = gh_manager::lookup (h); + + if (handle.ok ()) + { + graphics_object go = gh_manager::get_object (handle); + + if (max_arg_index >= 2 + && compare_property_values (go.get (pname), + args(2))) + waitfor_results[id] = true; + else + { + + frame.add_fcn (cleanup_waitfor_postset_listener, + ov_listener); + go.add_property_listener (pname, ov_listener, + POSTSET); + go.add_property_listener (pname, ov_listener, + PERSISTENT); + + if (go.get_properties () + .has_dynamic_property (pname)) + { + static octave_value wf_del_listener; + + if (! wf_del_listener.is_defined ()) + wf_del_listener = + octave_value (new octave_builtin + (waitfor_del_listener, + "waitfor_del_listener")); + + Cell del_listener (1, 4); + + del_listener(0) = wf_del_listener; + del_listener(1) = octave_uint32 (id); + del_listener(2) = h; + del_listener(3) = pname; + + octave_value ov_del_listener (del_listener); + + frame.add_fcn (cleanup_waitfor_predelete_listener, + ov_del_listener); + go.add_property_listener (pname, ov_del_listener, + PREDELETE); + } + } + } + } + else if (error_state || pname.empty ()) + error ("waitfor: invalid property name, expected a non-empty string value"); + } + + if (! error_state + && timeout_index < 0 + && args.length () > (max_arg_index + 1)) + { + caseless_str s = args(max_arg_index + 1).string_value (); + + if (! error_state) + { + if (s.compare ("timeout")) + timeout_index = max_arg_index + 1; + else + error ("waitfor: invalid parameter `%s'", s.c_str ()); + } + else + error ("waitfor: invalid parameter, expected `timeout'"); + } + + if (! error_state && timeout_index >= 0) + { + if (args.length () > (timeout_index + 1)) + { + timeout = static_cast + (args(timeout_index + 1).scalar_value ()); + + if (! error_state) + { + if (timeout < 1) + { + warning ("waitfor: the timeout value must be >= 1, using 1 instead"); + timeout = 1; + } + } + else + error ("waitfor: invalid timeout value, expected a value >= 1"); + } + else + error ("waitfor: missing timeout value"); + } + + // FIXME: There is still a "hole" in the following loop. The code + // assumes that an object handle is unique, which is a fair + // assumptions, except for figures. If a figure is destroyed + // then recreated with the same figure ID, within the same + // run of event hooks, then the figure destruction won't be + // caught and the loop will not stop. This is an unlikely + // possibility in practice, though. + // + // Using deletefcn callback is also unreliable as it could be + // modified during a callback execution and the waitfor loop + // would not stop. + // + // The only "good" implementation would require object + // listeners, similar to property listeners. + + time_t start = 0; + + if (timeout > 0) + start = time (0); + + while (! error_state) + { + if (true) + { + gh_manager::auto_lock guard; + + graphics_handle handle = gh_manager::lookup (h); + + if (handle.ok ()) + { + if (! pname.empty () && waitfor_results[id]) + break; + } + else + break; + } + +#if defined (WIN32) && ! defined (__CYGWIN__) + Sleep (100); +#else + octave_usleep (100000); +#endif + + OCTAVE_QUIT; + + command_editor::run_event_hooks (); + + if (timeout > 0) + { + if (start + timeout < time (0)) + break; + } + } + } + else + error ("waitfor: invalid handle value."); + } + else + print_usage (); + + return octave_value (); +} diff -r 7b83576b3b48 -r 3b654a0753b1 src/graphics.h.in --- a/src/graphics.h.in Wed Nov 23 00:20:00 2011 -0500 +++ b/src/graphics.h.in Wed Nov 23 08:59:25 2011 +0000 @@ -333,7 +333,7 @@ class property; -enum listener_mode { POSTSET, PERSISTENT }; +enum listener_mode { POSTSET, PERSISTENT, PREDELETE }; class base_property {