# HG changeset patch
# User Pantxo Diribarne <pantxo.diribarne@gmail.com>
# Date 1516566381 -3600
#      Sun Jan 21 21:26:21 2018 +0100
# Node ID d855ee6e350bd0398b0ae9b93c5990d67db1e2f6
# Parent  89b0a53258b767ae15640aa580b0ffdebebcc8f5
Document and fix "busyaction" an "interruptible" properties (bug #52804)

* graphics.cc (gh_manager::do_execute_listener): don't take "interruptible" into
  account for listeners (revert a56d283ff18a)
  (gh_manager::do_execute_callback): make sure the mutex is unlocked after the 
  graphics object has been accessed.
  (F__get_frame__): process graphics events for ML compatibility

* genpropdoc.m: document "busyaction" and "interruptible" properties

* plot.txi (Callback section): document how the graphics callbacks are executed
  depending on busyaction and interruptible properties

diff -r 89b0a53258b7 -r d855ee6e350b doc/interpreter/genpropdoc.m
--- a/doc/interpreter/genpropdoc.m	Thu Jan 18 13:13:53 2018 +0000
+++ b/doc/interpreter/genpropdoc.m	Sun Jan 21 21:26:21 2018 +0100
@@ -136,6 +136,14 @@
     switch (field)
       case "beingdeleted"
       case "busyaction"
+        s.doc = "Define how Octave handles the execution of callback \
+properties that would interrupt another callback. \n\
+The __prop__ property of the interrupting callback object indicates wether the \
+interrupting callback is queued \(@code{queue}, the default) or discarded \
+(@code{cancel}) when the executing callback object has its \
+@code{interruptible} property \@code{off}. \n\
+@xref{Callbacks, , @w{Callbacks section}}.";
+        
       case "buttondownfcn"
         s.doc = "__fcnmsg__";
         s.valid = valid_fcn;
@@ -174,6 +182,12 @@
 property.  @xref{XREF__objname__pickableparts, , @w{pickableparts property}}.";
 
       case "interruptible"
+        s.doc = "Specify whether this object's callback functions may be \
+interrupted by other callbacks. By default __prop__ is @code{on} \
+and callbacks that make use of @code{drawnow}, @code{figure}, @code{waitfor}, \
+@code{getframe} or @code{pause} functions are eventually interrupted.\n\
+@xref{Callbacks, , @w{Callbacks section}}.";
+        
       case "parent"
         s.doc = "Handle of the parent graphics object.";
         s.valid = valid_handle;
diff -r 89b0a53258b7 -r d855ee6e350b doc/interpreter/plot.txi
--- a/doc/interpreter/plot.txi	Thu Jan 18 13:13:53 2018 +0000
+++ b/doc/interpreter/plot.txi	Sun Jan 21 21:26:21 2018 +0100
@@ -1904,12 +1904,29 @@
 @group
 plot (x, "DeleteFcn", @{@@mycallback, "1"@})
 @dots{}
-function mycallback (h, e, a1)
+function mycallback (h, evt, a1)
   fprintf ("Closing plot %d\n", a1);
 endfunction
 @end group
 @end example
 
+@strong{Caution:} the second @code{evt} argument in callback functions is only
+loosely implemented in the Qt graphics toolkit:
+
+@itemize @bullet
+@item Mouse click events:
+@code{evt} is a class @code{double} value, 1 for left, 2 for middle and 3
+for right click.
+
+@item Key press events:
+@code{evt} is a structure with fields @code{Key} (string), @code{Character}
+(string) and @code{Modifier} (cell array of strings).
+
+@item Other events:
+@code{evt} is a class @code{double} empty matrix.
+@end itemize
+
+
 The basic callback functions that are available for all graphics objects
 are
 
@@ -1930,26 +1947,31 @@
 callback.
 @end itemize
 
-@strong{Caution:} the second @code{evt} argument in callback functions is only
-loosely implemented in the Qt graphics toolkit:
-
-@itemize @bullet
-@item Mouse click events:
-@code{evt} is a class @code{double} value, 1 for left, 2 for middle and 3
-for right click.
-
-@item Key press events:
-@code{evt} is a structure with fields @code{Key} (string), @code{Character}
-(string) and @code{Modifier} (cell array of strings).
-
-@item Other events:
-@code{evt} is a class @code{double} empty matrix.
-@end itemize
-
-
-The object and figure that the event occurred in that resulted in the
-callback being called can be found with the @code{gcbo} and @code{gcbf}
-functions.
+By default callback functions are queued (they are executed one
+after the other in the event queue) unless the @code{drawnow}, @code{figure}, 
+@code{waitfor}, @code{getframe} or @code{pause} functions are called.
+If an executing callback calls one of those functions, it causes Octave to 
+flush the event queue, which results in the executing callback being 
+interrupted.
+
+It is possible to specify that an ocject's callbacks should not be interrupted
+by setting the object's @code{interruptible} property @code{off}. In this
+case Octave decides what to do based on the @code{busyaction} property of the
+@strong{interrupting} callback object:
+@table @asis
+@item @code{queue} (the default)
+The interrupting callback is executed after the executing callback has returned.
+@item @code{cancel}
+The interrupting callback is discarded.
+@end table
+
+The @code{interruptible} property has no effect when the interrupting callback 
+is a @code{deletefcn} or a figure @code{resizefcn} or @code{closerequestfcn}. 
+Those always interrupt the executing callback.
+
+The handle to the object that holds the callback being executed can be 
+obtained with the @code{gcbo} function.  The handle to the ancestor figure 
+of this object may be obtained using the @code{gcbf} function.
 
 @DOCSTRING(gcbo)
 
diff -r 89b0a53258b7 -r d855ee6e350b libinterp/corefcn/graphics.cc
--- a/libinterp/corefcn/graphics.cc	Thu Jan 18 13:13:53 2018 +0000
+++ b/libinterp/corefcn/graphics.cc	Sun Jan 21 21:26:21 2018 +0100
@@ -10223,20 +10223,6 @@
 gh_manager::do_execute_listener (const graphics_handle& h,
                                  const octave_value& l)
 {
-  if (! callback_objects.empty ())
-    {        
-      const graphics_object& current = callback_objects.front ();
-
-      if (current.valid_object () 
-          && ! current.get_properties ().is_interruptible ())
-        {
-          if (get_object (h).get_properties ().busyaction_is ("queue"))
-            do_post_event (graphics_event::create_callback_event (h, l));
-
-          return;
-        }
-    }
-
   if (octave::thread::is_thread ())
     gh_manager::execute_callback (h, l, octave_value ());
   else
@@ -10266,11 +10252,16 @@
       octave::unwind_protect_safe frame;
       frame.add_fcn (gh_manager::restore_gcbo);
 
-      gh_manager::auto_lock guard;
       graphics_object go (get_object (h));
-      callback_objects.push_front (go);
-      if (go.get ("handlevisibility").string_value () != "off")
-        xset_gcbo (h);
+      if (go)
+        {
+          // FIXME: is the lock necessary when we're only calling a const 
+          //        "get" method.
+          gh_manager::auto_lock guard;
+          callback_objects.push_front (go);
+          if (go.get ("handlevisibility").string_value () != "off")
+            xset_gcbo (h);
+        }
 
       // Copy CB because "function_value" method is non-const.
 
@@ -12460,5 +12451,8 @@
   if (! go || ! go.isa ("figure"))
     error ("__get_frame__: HFIG is not a figure");
 
+  // For Matlab compatibility getframe must flush the event queue
+  gh_manager::process_events ();
+
   return ovl (go.get_toolkit ().get_pixels (go));
 }
