changeset 24498:5865d2fef424

legend.m: Clean up implementation. * legend.m: Rename variable "fig" to "hfig" for clarity. Remove unnecessary error() that should never happen (someone will always report it if it does). Add more comments to try and explain code. Rename property "handle" to "__axes_handle__" which points from legend to associated axes. Add new property "__legend_handle__" to associated axes which points back to legend. Replace complicated searches for an existing legend object with try/catch block looking for "__legend_handle__" property. Remove redundanc code. Rename "oldfig" to "origfig" for clarity. Rename "curaxes" to "origaxes" for clarity. Important: Make new legend object's parent the same as the parent of the associated axes. This makes legends work for things like uipanel, not just figures. For performance, limit search for htdel text object to a depth of 1. * legend.m (cb_legend_update): Rename "handle" to "__axes_handle__". Remove unnecessarily complicated code to get hplots from existing legend. * legend.m (cb_restore_axes): Set "__legend_handle__" on associated axes to [] when deleting legend. * __getlegenddata__.m, axes.m: Rename property "handle" to "__axes_handle__". * __errplot__.m: Remove unnecessary ancestor() call to find axes. Replace search for legend with try/catch on "__legend_handle__" property. * __plt__.m: Replace search for legend with try/catch on "__legend_handle__" property. * hdl2struct.m: Replace search for legend with try/catch on "__legend_handle__" property. Replace search for colorbar with try/catch on "__colorbar_handle__" * __gnuplot_draw_figure__.m: Rename property "handle" to "__axes_handle__". Replace search for legend with try/catch on "__legend_handle__" property. * __gnuplot_print__.m (get_figure_text): Simplify determination of legend, it is just an axes object with tag set to legend.
author Rik <rik@octave.org>
date Wed, 03 Jan 2018 08:10:48 -0800
parents 1c96b44feb7a
children 941ea3da921f
files scripts/plot/appearance/__getlegenddata__.m scripts/plot/appearance/legend.m scripts/plot/draw/private/__errplot__.m scripts/plot/draw/private/__plt__.m scripts/plot/util/axes.m scripts/plot/util/hdl2struct.m scripts/plot/util/private/__gnuplot_draw_figure__.m scripts/plot/util/private/__gnuplot_print__.m
diffstat 8 files changed, 88 insertions(+), 104 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/plot/appearance/__getlegenddata__.m	Tue Jan 02 15:39:25 2018 -0800
+++ b/scripts/plot/appearance/__getlegenddata__.m	Wed Jan 03 08:10:48 2018 -0800
@@ -25,7 +25,7 @@
 
   hplots = [];
   text_strings = {};
-  ca = getappdata (hlegend, "handle");
+  ca = getappdata (hlegend, "__axes_handle__");
   if (numel (ca) == 1)
     kids = get (ca, "children");
   else
--- a/scripts/plot/appearance/legend.m	Tue Jan 02 15:39:25 2018 -0800
+++ b/scripts/plot/appearance/legend.m	Wed Jan 03 08:10:48 2018 -0800
@@ -133,9 +133,9 @@
 ## @code{FontSize} to which it is attached.  Use @code{set} to override this
 ## if necessary.
 ##
-## A legend is implemented as an additional axes object of the current figure
-## with the @code{tag} property set to @qcode{"legend"}.  Properties of the
-## legend object may be manipulated directly by using @code{set}.
+## A legend is implemented as an additional axes object with the @code{tag}
+## property set to @qcode{"legend"}.  Properties of the legend object may be
+## manipulated directly by using @code{set}.
 ## @end deftypefn
 
 function [hleg, hleg_obj, hplot, labels] = legend (varargin)
@@ -148,11 +148,11 @@
     if (isempty (ca))
       ca = gca ();
     endif
-    fig = ancestor (ca, "figure");
+    hfig = ancestor (ca, "figure");
   else
-    fig = get (0, "currentfigure");
-    if (isempty (fig))
-      fig = gcf ();
+    hfig = get (0, "currentfigure");
+    if (isempty (hfig))
+      hfig = gcf ();
     endif
     ca = gca ();
   endif
@@ -160,9 +160,6 @@
   ## Special handling for plotyy which has two axes objects
   if (isprop (ca, "__plotyy_axes__"))
     plty = get (ca, "__plotyy_axes__");
-    if (! all (ishghandle (plty)))
-      error ("legend.m: This should not happen.  File a bug report.");
-    endif
     ca = [ca, plty.'];
     ## Remove duplicates while preserving order
     [~, n] = unique (ca, "first");
@@ -170,9 +167,11 @@
   endif
 
   if (nargin > 0 && all (ishghandle (varargin{1})))
+    ## List of plot objects to label given as first argument
     kids = flipud (varargin{1}(:));
     varargin(1) = [];
   else
+    ## Find list of plot objects from axes "children"
     kids = ca;
     kids(strcmp (get (ca, "tag"), "legend")) = [];
     if (isscalar (kids))
@@ -184,17 +183,15 @@
   nargs = numel (varargin);
   nkids = numel (kids);
 
-  ## Find any existing legend object on figure
+  ## Find any existing legend object associated with axes
   hlegend = [];
-  fkids = get (fig, "children");
-  for i = 1 : numel (fkids)
-    if (strcmp (get (fkids(i), {"type", "tag"}), {"axes", "legend"}))
-      handle = getappdata (fkids(i), "handle");
-      if (any (ismember (handle, ca)))
-        hlegend = fkids(i);
+  for hax = ca
+    try
+      hlegend = get (hax, "__legend_handle__");
+      if (! isempty (hlegend))
         break;
       endif
-    endif
+    end_try_catch
   endfor
 
   orientation = "default";
@@ -397,9 +394,6 @@
     endif
   else
     ## Create or modify legend object
-    hobjects = [];
-    hplots = [];
-    text_strings = {};
 
     if (! isempty (hlegend))
       ## Disable callbacks while modifying an existing legend
@@ -503,8 +497,7 @@
     if (isempty (hplots))
       ## Nothing to label
       if (! isempty (hlegend))
-        fkids = get (fig, "children");
-        delete (fkids(fkids == hlegend));
+        delete (hlegend);
         hlegend = [];
         hobjects = [];
         hplots = [];
@@ -582,28 +575,36 @@
 
       linelength = 15;
 
-      ## Create the axes object first
-      oldfig = get (0, "currentfigure");
-      if (oldfig != fig)
-        set (0, "currentfigure", fig);
+      ## Preamble code to restore figure and axes after legend creation
+      origfig = get (0, "currentfigure");
+      if (origfig != hfig)
+        set (0, "currentfigure", hfig);
       else
-        oldfig = [];
+        origfig = [];
       endif
-      curaxes = get (fig, "currentaxes");
+      origaxes = get (hfig, "currentaxes");
       unwind_protect
         ud = ancestor (hplots, "axes");
         if (! isscalar (ud))
           ud = unique ([ud{:}]);
         endif
+        hpar = get (ud(1), "parent");
+
         if (isempty (hlegend))
           ## Create a legend object (axes + new properties)
           addprops = true;
-          hlegend = axes ("tag", "legend",
+          hlegend = axes ("parent", hpar, "tag", "legend",
                           "box", box,
                           "xtick", [], "ytick", [],
                           "xlim", [0, 1], "ylim", [0, 1],
                           "activepositionproperty", "position");
-          setappdata (hlegend, "handle", ud);
+          setappdata (hlegend, "__axes_handle__", ud);
+          try
+            addproperty ("__legend_handle__", ud(1), "handle", hlegend);
+          catch
+            set (ud(1), "__legend_handle__", hlegend);
+          end_try_catch
+
           ## Inherit fontsize from current axis
           ## "fontunits" should be first because it affects interpretation
           ## of "fontsize" property.
@@ -618,11 +619,12 @@
           axes (hlegend);
           delete (get (hlegend, "children"));
           ## Hack: get list of hplots for which addlistener has been called.
-          old_hplots = [ get(hlegend, "deletefcn"){6:end} ];
+          old_hplots = get (hlegend, "deletefcn"){6};
         endif
 
         if (addprops)
           ## Only required for a newly created legend object
+          ## FIXME: "autoupdate" is not implemented.
           addproperty ("autoupdate", hlegend, "radio", "{on}|off");
           addproperty ("edgecolor", hlegend, "color", [0.15, 0.15, 0.15]);
           addproperty ("textcolor", hlegend, "color", [0, 0, 0]);
@@ -720,7 +722,7 @@
           lpos = [0, 0, num1 * xstep, num2 * ystep];
         endif
 
-        gnuplot = strcmp (get (fig, "__graphics_toolkit__"), "gnuplot");
+        gnuplot = strcmp (get (hfig, "__graphics_toolkit__"), "gnuplot");
         if (gnuplot)
           ## gnuplot places the key (legend) at edge of the figure window.
           ## OpenGL places the legend box at edge of the unmodified axes
@@ -984,13 +986,13 @@
 
         ## Add an invisible text object to original axis
         ## that, when it is destroyed, will remove the legend.
-        props = {"parent", ca(1), "tag", "deletelegend", ...
-                 "visible", "off", "handlevisibility", "off", ...
-                 "xliminclude", "off", "yliminclude", "off", ...
-                 "zliminclude", "off"};
-        htdel = findall (ca(1), "tag", "deletelegend", "type", "text");
+        htdel = findall (ca(1), "-depth", 1, "tag", "deletelegend",
+                                "type", "text");
         if (isempty (htdel))
-          htdel = text (0, 0, "", props{:});
+          htdel = text (0, 0, "", "parent", ca(1), "tag", "deletelegend", 
+                        "visible", "off", "handlevisibility", "off",
+                        "xliminclude", "off", "yliminclude", "off",
+                        "zliminclude", "off");
           set (htdel, "deletefcn", {@cb_axes_deleted, ca, hlegend});
         endif
         if (isprop (hlegend, "unmodified_axes_position"))
@@ -1094,10 +1096,11 @@
           addlistener (hlegend, "string", @cb_legend_update);
           addlistener (hlegend, "textposition", @cb_legend_update);
         endif
+
       unwind_protect_cleanup
-        set (fig, "currentaxes", curaxes);
-        if (! isempty (oldfig))
-          set (0, "currentfigure", oldfig);
+        set (hfig, "currentaxes", origaxes);
+        if (! isempty (origfig))
+          set (0, "currentfigure", origfig);
         endif
       end_unwind_protect
     endif
@@ -1122,9 +1125,9 @@
   if (! recursive)
     recursive = true;
     unwind_protect
-      hax = getappdata (hleg, "handle");
+      hax = getappdata (hleg, "__axes_handle__");
       ## Hack.  Maybe store this somewhere else such as appdata.
-      hplots = [ get(hleg, "deletefcn"){6:end} ];
+      hplots = get (hleg, "deletefcn"){6};
       text_strings = get (hleg, "string");
       position = get (hleg, "unmodified_axes_position");
       outerposition = get (hleg, "unmodified_axes_outerposition");
@@ -1200,6 +1203,7 @@
   endif
 
 endfunction
+
 ## Axes to which legend was attached is being deleted/reset.  Delete legend.
 function cb_axes_deleted (~, ~, ca, hlegend)
   if (isaxes (hlegend))
@@ -1257,6 +1261,9 @@
     endif
   endfor
 
+  ## Nullify legend link (can't delete properties yet)
+  set (ca(1), "__legend_handle__", []);
+
 endfunction
 
 ## Update legend item because underlying plot line object has changed.
@@ -1748,10 +1755,10 @@
 %!   hax2 = subplot (1,2,2);
 %!   plot (1:10);
 %!   hleg1 = legend (hax1, "foo");
-%!   assert (getappdata (hleg1, "handle"), hax1);
+%!   assert (getappdata (hleg1, "__axes_handle__"), hax1);
 %!   assert (gca (), hax2);
 %!   hleg2 = legend ("bar");
-%!   assert (getappdata (hleg2, "handle"), gca ());
+%!   assert (getappdata (hleg2, "__axes_handle__"), gca ());
 %! unwind_protect_cleanup
 %!   close (h);
 %! end_unwind_protect
--- a/scripts/plot/draw/private/__errplot__.m	Tue Jan 02 15:39:25 2018 -0800
+++ b/scripts/plot/draw/private/__errplot__.m	Wed Jan 03 08:10:48 2018 -0800
@@ -242,7 +242,6 @@
     addlistener (hg, "xudata", fcn);
     addlistener (hg, "format", fcn);
 
-    hax = ancestor (hg, "axes");
     addlistener (hax, "xscale", fcn);
     addlistener (hax, "yscale", fcn);
 
@@ -252,17 +251,11 @@
 
   ## Process legend key
   if (! isempty (fmt.key) && nplots > 0)
-    hlegend = [];
-    fkids = get (gcf (), "children");
-    for i = 1 : numel (fkids)
-      if (strcmp (get (fkids(i), {"type", "tag"}), {"axes", "legend"}))
-        leghandle = getappdata (fkids(i), "handle");
-        if (! isempty (intersect (leghandle, gca ())))
-          hlegend = fkids(i);
-          break;
-        endif
-      endif
-    endfor
+    try
+      hlegend = get (hax, "__legend_handle__");
+    catch
+      hlegend = [];
+    end_try_catch
 
     if (isempty (hlegend))
       hlgnd = [];
--- a/scripts/plot/draw/private/__plt__.m	Tue Jan 02 15:39:25 2018 -0800
+++ b/scripts/plot/draw/private/__plt__.m	Wed Jan 03 08:10:48 2018 -0800
@@ -37,17 +37,12 @@
     property_set = false;
     properties = {};
 
-    hlegend = [];
-    fkids = get (gcf (), "children");
-    for i = 1 : numel (fkids)
-      if (strcmp (get (fkids(i), {"type", "tag"}), {"axes", "legend"}))
-        leghandle = getappdata (fkids(i), "handle");
-        if (! isempty (intersect (leghandle, gca ())))
-          hlegend = fkids(i);
-          break;
-        endif
-      endif
-    endfor
+    ## Find any legend associated with this axes
+    try
+      hlegend = get (h, "__legend_handle__");
+    catch
+      hlegend = [];
+    end_try_catch
 
     setlgnd = false;
     if (isempty (hlegend))
--- a/scripts/plot/util/axes.m	Tue Jan 02 15:39:25 2018 -0800
+++ b/scripts/plot/util/axes.m	Wed Jan 03 08:10:48 2018 -0800
@@ -108,9 +108,9 @@
     if (any (hleg))
       ## Get axes handles associated with legend
       if (isscalar (hleg))
-        hlegaxes = getappdata (hleg, "handle");
+        hlegaxes = getappdata (hleg, "__axes_handle__");
       else
-        hlegaxes = [getappdata(hleg, "handle"){:}](:);
+        hlegaxes = [getappdata(hleg, "__axes_handle__"){:}](:);
       endif
       hleg = hleg(hlegaxes == h);
       h = [hleg; h];
--- a/scripts/plot/util/hdl2struct.m	Tue Jan 02 15:39:25 2018 -0800
+++ b/scripts/plot/util/hdl2struct.m	Wed Jan 03 08:10:48 2018 -0800
@@ -79,33 +79,31 @@
     if (strcmp (s.type, "axes") && isempty (get (h, "tag")))
       ## look for legends and colorbars among axes brothers and add them
       ## to the children list
-
-      par = get (h, "parent");
-      lg = findobj (par, "-depth", 1, "tag", "legend");
-      if (! isempty (lg))
-        ## identify legends which are attached to this axes.
-        idx = ([[get(lg).userdata].handle] == h);
-        lg = lg(idx);
-      endif
+      try
+        lg = get (h, "__legend_handle__");
+      catch
+        lg = [];
+      end_try_catch
       nlg = length (lg);
       if (nlg == 1)
         ii += 1;
         s.children(ii) = hdl2struct (lg);
       elseif (nlg > 1)
+        ## FIXME: Unreachable code now.  Delete?
         error ("hdl2struct: more than one legend found");
       endif
 
-      cb = findobj (par, "-depth", 1, "tag", "colorbar");
-      if (! isempty (cb))
-        ## identify colorbars which are attached to this axes.
-        idx = ([get(cb).axes] == h);
-        cb = cb(idx);
-      endif
+      try
+        cb = get (h, "__colorbar_handle__");
+      catch
+        cb = [];
+      end_try_catch
       ncb = length (cb);
       if (ncb == 1)
         ii += 1;
         s.children(ii) = hdl2struct (cb);
       elseif (ncb > 1)
+        ## FIXME: Unreachable code now.  Delete?
         error ("hdl2struct: more than one colorbar found");
       endif
     endif
--- a/scripts/plot/util/private/__gnuplot_draw_figure__.m	Tue Jan 02 15:39:25 2018 -0800
+++ b/scripts/plot/util/private/__gnuplot_draw_figure__.m	Wed Jan 03 08:10:48 2018 -0800
@@ -49,11 +49,11 @@
         type = get (kids(i), "type");
         switch (type)
           case "axes"
-            if (strcmpi (get (kids (i), "tag"), "legend"))
+            if (strcmp (get (kids(i), "tag"), "legend"))
               ## This is so ugly.  If there was a way of getting
               ## gnuplot to give us the text extents of strings
               ## then we could get rid of this mess.
-              lh = getappdata (kids(i), "handle");
+              lh = getappdata (kids(i), "__axes_handle__");
               if (isscalar (lh))
                 ## We have a legend with a single parent.  It'll be handled
                 ## below as a gnuplot key to the axis it corresponds to.
@@ -155,19 +155,12 @@
                 endif
                 ## Find if this axes has an associated legend axes and pass it
                 ## to __gnuplot_draw_axes__
-                hlegend = [];
-                fkids = get (h, "children");
-                for j = 1 : numel (fkids)
-                  if (ishghandle (fkids (j))
-                      && strcmp (get (fkids(j), {"type", "tag"}),
-                                 {"axes", "legend"}))
-                    leghandle = getappdata (fkids(j), "handle");
-                    if (! isempty (intersect (leghandle, kids(i))))
-                      hlegend = get (fkids(j));
-                      break;
-                    endif
-                  endif
-                endfor
+                try
+                  hlegend = get (kids(i), "__legend_handle__");
+                  hlegend = get (hlegend);
+                catch
+                  hlegend = [];
+                end_try_catch
                 __gnuplot_draw_axes__ (kids(i), plot_stream, enhanced,
                                        bg_is_set, fg_is_set, hlegend);
               unwind_protect_cleanup
--- a/scripts/plot/util/private/__gnuplot_print__.m	Tue Jan 02 15:39:25 2018 -0800
+++ b/scripts/plot/util/private/__gnuplot_print__.m	Wed Jan 03 08:10:48 2018 -0800
@@ -354,9 +354,7 @@
   ## Do not change the text objects fontsizes for the children of a
   ## legend axes.  These will be handled by the fontsize listener.
   is_legend_key_string = strcmp (get (hp, "tag"), "legend") ...
-                       & isprop (hp, "string") ...
-                       & isprop (hp, "location") ...
-                       & strcmp (get (hp, "type"), "axes");
+                         & strcmp (get (hp, "type"), "axes");
   h(is_legend_key_string) = [];
   fontsize = get (h, "fontsize");
   switch (numel (fontsize))