# HG changeset patch
# User Pantxo Diribarne <pantxo.diribarne@gmail.com>
# Date 1512912906 -3600
#      Sun Dec 10 14:35:06 2017 +0100
# Node ID ecbe3b4fe459e07a6ef554983e3ce73538e10826
# Parent  222addbadf46f46805ea505171e2ea86243beec2
Better handling of "handlevisibility" property (bug #52621)

* graphics.in.h (base_property::handlevisibility): declare virtual updater
  (figure::properties::update_handlevisibility): declare overloaded method
* graphics.cc (base_properties::update_handlevisibility): don't let objects
  with handlevisibility = off be the root callbaackobject or figure 
  currentobject. Add BIST for handlevisibility property.
  (figure::properties::update_handlevisibility): don't let figure with
  handlevisibility = off be the root currentfigure
  (axes::properties::update_handlevisibility): don't let axes with 
  handlevisibility = off be the figure currentaxes
* figure.m: don't let figure become "currentfigure" if its handle is not visible

diff -r 222addbadf46 -r ecbe3b4fe459 libinterp/corefcn/graphics.cc
--- a/libinterp/corefcn/graphics.cc	Sun Dec 03 12:37:13 2017 +0100
+++ b/libinterp/corefcn/graphics.cc	Sun Dec 10 14:35:06 2017 +0100
@@ -2057,6 +2057,28 @@
     }
 }
 
+void
+figure::properties::update_handlevisibility (void)
+{
+  if (! is_handle_visible ())
+    {
+      octave_value cf = gh_manager::get_object (0).get ("currentfigure");
+      if (! cf.isempty () && cf.double_value () == __myhandle__)
+        {
+          gh_manager::auto_lock guard;
+          octave_value kids =  gh_manager::get_object (0).get ("children");
+          if (kids.isempty ())
+            gh_manager::get_object (0).set ("currentfigure", Matrix ());
+          else
+            {
+              NDArray kidsarray = kids.array_value ();
+              gh_manager::get_object (0).set ("currentfigure", kidsarray(0));
+            }
+        }
+    }
+
+  base_properties::update_handlevisibility ();
+}
 // ---------------------------------------------------------------------
 
 void
@@ -3283,6 +3305,78 @@
 }
 
 void
+base_properties::update_handlevisibility (void)
+{
+  if (is_handle_visible ())
+    return;
+  
+  // This object should not be the root "callbackobject"
+  graphics_object rt = gh_manager::get_object (0);
+  octave_value cbo = rt.get ("callbackobject");
+  if (! cbo.isempty () && cbo.double_value () == __myhandle__)
+    {
+      gh_manager::auto_lock guard;
+      auto& root_props =
+        dynamic_cast<root_figure::properties&> (rt.get_properties ());
+      root_props.set_callbackobject (Matrix ());
+    }
+
+  // This object should not be the figure "currentobject"
+  graphics_object go (gh_manager::get_object (get___myhandle__ ()));
+  graphics_object fig (go.get_ancestor ("figure"));
+  if (fig.valid_object ())
+    {
+      octave_value co = fig.get ("currentobject");
+      if (! co.isempty () && co.double_value () == __myhandle__)
+        {
+          gh_manager::auto_lock guard;
+          auto& fig_props =
+            dynamic_cast<figure::properties&> (fig.get_properties ());
+          fig_props.set_currentobject (Matrix ());
+        }
+    }
+}
+
+/*
+## test current figure and current axes have visible handles 
+%!test
+%! hf1 = figure ("visible", "off");
+%! hf2 = figure ("visible", "off");
+%! hax1 = axes ();
+%! hax2 = axes ();
+%! unwind_protect
+%!   assert (get (0, "currentfigure"), hf2);
+%!   assert (get (hf2, "currentaxes"), hax2);
+%!   set (hf2, "handlevisibility", "off");
+%!   assert (get (0, "currentfigure"), hf1);
+%!   set (hax2, "handlevisibility", "off");
+%!   assert (get (hf2, "currentaxes"), hax1);
+%!   assert (get (hf2, "currentobject"), []);
+%! unwind_protect_cleanup
+%!   close ([hf1, hf2]);
+%! end_unwind_protect;
+*/
+
+/*
+## test current callback object have visible handle 
+%!test
+%! hf = figure ("visible", "off");
+%! hax = axes ();
+%! unwind_protect
+%!   fcn = @(h) assert (gcbo (), h);
+%!   addlistener (hax, "color", fcn);
+%!   set (hax, "color", "r");
+%!   dellistener (hax, "color", fcn);
+%!   set (hax, "handlevisibility", "off");
+%!   fcn = @() assert (gcbo (), []);
+%!   addlistener (hax, "color", fcn);
+%!   set (hax, "color", "b");
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect;
+*/
+
+void
 base_properties::add_listener (const caseless_str& pname,
                                const octave_value& val,
                                listener_mode mode)
@@ -8486,6 +8580,31 @@
 }
 
 void
+axes::properties::update_handlevisibility (void)
+{
+  if (! is_handle_visible ())
+    {
+      graphics_object go (gh_manager::get_object (get___myhandle__ ()));
+      graphics_object fig (go.get_ancestor ("figure"));
+      octave_value ca = fig.get ("currentaxes");
+      if (! ca.isempty () && ca.double_value () == __myhandle__)
+        {
+          gh_manager::auto_lock guard;
+          octave_value kids =  fig.get ("children");
+          if (kids.isempty ())
+            fig.set ("currentaxes", Matrix ());
+          else
+            {
+              NDArray kidsarray = kids.array_value ();
+              fig.set ("currentaxes", kidsarray(0));
+            }
+        }
+    }
+
+  base_properties::update_handlevisibility ();
+}
+
+void
 axes::properties::clear_zoom_stack (bool do_unzoom)
 {
   size_t items_to_leave_on_stack = (do_unzoom ? 7 : 0);
@@ -10133,13 +10252,11 @@
       octave::unwind_protect_safe frame;
       frame.add_fcn (gh_manager::restore_gcbo);
 
-      if (true)
-        {
-          gh_manager::auto_lock guard;
-
-          callback_objects.push_front (get_object (h));
-          xset_gcbo (h);
-        }
+      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);
 
       // Copy CB because "function_value" method is non-const.
 
diff -r 222addbadf46 -r ecbe3b4fe459 libinterp/corefcn/graphics.in.h
--- a/libinterp/corefcn/graphics.in.h	Sun Dec 03 12:37:13 2017 +0100
+++ b/libinterp/corefcn/graphics.in.h	Sun Dec 10 14:35:06 2017 +0100
@@ -2362,7 +2362,7 @@
     bool_property clipping , "on"
     callback_property createfcn , Matrix ()
     callback_property deletefcn , Matrix ()
-    radio_property handlevisibility , "{on}|callback|off"
+    radio_property handlevisibility u , "{on}|callback|off"
     bool_property hittest , "on"
     bool_property interruptible , "on"
     handle_property parent fs , p
@@ -2378,6 +2378,8 @@
     graphics_handle __myhandle__ fhrs , mh
   END_PROPERTIES
 
+    virtual void update_handlevisibility (void);
+
 protected:
   struct cmp_caseless_str
   {
@@ -3202,6 +3204,8 @@
         paperposition.set (get_auto_paperposition ());
     }
 
+    void update_handlevisibility (void);
+
     mutable graphics_toolkit toolkit;
   };
 
@@ -3499,6 +3503,7 @@
                       bool push_to_zoom_stack = true);
 
     void unzoom (void);
+    void update_handlevisibility (void);
     void push_zoom_stack (void);
     void clear_zoom_stack (bool do_unzoom = true);
 
diff -r 222addbadf46 -r ecbe3b4fe459 scripts/plot/util/figure.m
--- a/scripts/plot/util/figure.m	Sun Dec 03 12:37:13 2017 +0100
+++ b/scripts/plot/util/figure.m	Sun Dec 10 14:35:06 2017 +0100
@@ -90,7 +90,10 @@
     set (f, varargin{:});
   endif
 
-  set (0, "currentfigure", f);
+  if (strcmp (get (f, "handlevisibility"), "on"))
+    set (0, "currentfigure", f);
+  endif
+  
   ## When switching to figure N, make figure visible and on top of stack,
   ## unless visibility is explicitly switched off.
   if (! init_new_figure && ! any (strcmpi (varargin(1:2:end), "visible")
