diff scripts/plot/appearance/legend.m @ 17572:7bb76a22cde1

maint: Split scripts/plot directory into 4 pieces. scripts/gui : user-interface functions scripts/plot/appearance : functions controlling plot appearance scripts/plot/draw : plotting functions which produce graphs scripts/plot/util : low-level plotting functions and utilities. * scripts/gui/guidata.m, scripts/gui/guihandles.m, scripts/gui/module.mk, scripts/gui/private/__file_filter__.m, scripts/gui/private/__fltk_file_filter__.m, scripts/gui/private/__is_function__.m, scripts/gui/private/__uigetdir_fltk__.m, scripts/gui/private/__uigetfile_fltk__.m, scripts/gui/private/__uiobject_split_args__.m, scripts/gui/private/__uiputfile_fltk__.m, scripts/gui/uicontextmenu.m, scripts/gui/uicontrol.m, scripts/gui/uigetdir.m, scripts/gui/uigetfile.m, scripts/gui/uimenu.m, scripts/gui/uipanel.m, scripts/gui/uipushtool.m, scripts/gui/uiputfile.m, scripts/gui/uiresume.m, scripts/gui/uitoggletool.m, scripts/gui/uitoolbar.m, scripts/gui/uiwait.m, scripts/gui/waitbar.m, scripts/gui/waitforbuttonpress.m: Moved from scripts/plot to scripts/gui * scripts/plot/appearance/__clabel__.m, scripts/plot/appearance/__getlegenddata__.m, scripts/plot/appearance/axis.m, scripts/plot/appearance/box.m, scripts/plot/appearance/caxis.m, scripts/plot/appearance/clabel.m, scripts/plot/appearance/daspect.m, scripts/plot/appearance/diffuse.m, scripts/plot/appearance/grid.m, scripts/plot/appearance/gtext.m, scripts/plot/appearance/hidden.m, scripts/plot/appearance/legend.m, scripts/plot/appearance/orient.m, scripts/plot/appearance/pbaspect.m, scripts/plot/appearance/private/__axis_label__.m, scripts/plot/appearance/private/__axis_limits__.m, scripts/plot/appearance/shading.m, scripts/plot/appearance/specular.m, scripts/plot/appearance/text.m, scripts/plot/appearance/title.m, scripts/plot/appearance/view.m, scripts/plot/appearance/whitebg.m, scripts/plot/appearance/xlabel.m, scripts/plot/appearance/xlim.m, scripts/plot/appearance/ylabel.m, scripts/plot/appearance/ylim.m, scripts/plot/appearance/zlabel.m, scripts/plot/appearance/zlim.m: Moved from scripts/plot to subdir appearance. * scripts/plot/draw/area.m, scripts/plot/draw/bar.m, scripts/plot/draw/barh.m, scripts/plot/draw/colorbar.m, scripts/plot/draw/comet.m, scripts/plot/draw/comet3.m, scripts/plot/draw/compass.m, scripts/plot/draw/contour.m, scripts/plot/draw/contour3.m, scripts/plot/draw/contourc.m, scripts/plot/draw/contourf.m, scripts/plot/draw/cylinder.m, scripts/plot/draw/ellipsoid.m, scripts/plot/draw/errorbar.m, scripts/plot/draw/ezcontour.m, scripts/plot/draw/ezcontourf.m, scripts/plot/draw/ezmesh.m, scripts/plot/draw/ezmeshc.m, scripts/plot/draw/ezplot.m, scripts/plot/draw/ezplot3.m, scripts/plot/draw/ezpolar.m, scripts/plot/draw/ezsurf.m, scripts/plot/draw/ezsurfc.m, scripts/plot/draw/feather.m, scripts/plot/draw/fill.m, scripts/plot/draw/fplot.m, scripts/plot/draw/hist.m, scripts/plot/draw/isocolors.m, scripts/plot/draw/isonormals.m, scripts/plot/draw/isosurface.m, scripts/plot/draw/line.m, scripts/plot/draw/loglog.m, scripts/plot/draw/loglogerr.m, scripts/plot/draw/mesh.m, scripts/plot/draw/meshc.m, scripts/plot/draw/meshz.m, scripts/plot/draw/pareto.m, scripts/plot/draw/patch.m, scripts/plot/draw/pcolor.m, scripts/plot/draw/peaks.m, scripts/plot/draw/pie.m, scripts/plot/draw/pie3.m, scripts/plot/draw/plot.m, scripts/plot/draw/plot3.m, scripts/plot/draw/plotmatrix.m, scripts/plot/draw/plotyy.m, scripts/plot/draw/polar.m, scripts/plot/draw/private/__add_datasource__.m, scripts/plot/draw/private/__bar__.m, scripts/plot/draw/private/__contour__.m, scripts/plot/draw/private/__errcomm__.m, scripts/plot/draw/private/__errplot__.m, scripts/plot/draw/private/__ezplot__.m, scripts/plot/draw/private/__interp_cube__.m, scripts/plot/draw/private/__line__.m, scripts/plot/draw/private/__marching_cube__.m, scripts/plot/draw/private/__patch__.m, scripts/plot/draw/private/__pie__.m, scripts/plot/draw/private/__plt__.m, scripts/plot/draw/private/__quiver__.m, scripts/plot/draw/private/__scatter__.m, scripts/plot/draw/private/__stem__.m, scripts/plot/draw/quiver.m, scripts/plot/draw/quiver3.m, scripts/plot/draw/rectangle.m, scripts/plot/draw/ribbon.m, scripts/plot/draw/rose.m, scripts/plot/draw/scatter.m, scripts/plot/draw/scatter3.m, scripts/plot/draw/semilogx.m, scripts/plot/draw/semilogxerr.m, scripts/plot/draw/semilogy.m, scripts/plot/draw/semilogyerr.m, scripts/plot/draw/shrinkfaces.m, scripts/plot/draw/slice.m, scripts/plot/draw/sombrero.m, scripts/plot/draw/sphere.m, scripts/plot/draw/stairs.m, scripts/plot/draw/stem.m, scripts/plot/draw/stem3.m, scripts/plot/draw/stemleaf.m, scripts/plot/draw/surf.m, scripts/plot/draw/surface.m, scripts/plot/draw/surfc.m, scripts/plot/draw/surfl.m, scripts/plot/draw/surfnorm.m, scripts/plot/draw/tetramesh.m, scripts/plot/draw/trimesh.m, scripts/plot/draw/triplot.m, scripts/plot/draw/trisurf.m, scripts/plot/draw/waterfall.m: Moved from plot/ to subdir draw. * scripts/plot/util/__actual_axis_position__.m, scripts/plot/util/__default_plot_options__.m, scripts/plot/util/__gnuplot_drawnow__.m, scripts/plot/util/__next_line_color__.m, scripts/plot/util/__next_line_style__.m, scripts/plot/util/__plt_get_axis_arg__.m, scripts/plot/util/__pltopt__.m, scripts/plot/util/allchild.m, scripts/plot/util/ancestor.m, scripts/plot/util/axes.m, scripts/plot/util/cla.m, scripts/plot/util/clf.m, scripts/plot/util/close.m, scripts/plot/util/closereq.m, scripts/plot/util/colstyle.m, scripts/plot/util/copyobj.m, scripts/plot/util/figure.m, scripts/plot/util/findall.m, scripts/plot/util/findfigs.m, scripts/plot/util/findobj.m, scripts/plot/util/gca.m, scripts/plot/util/gcbf.m, scripts/plot/util/gcbo.m, scripts/plot/util/gcf.m, scripts/plot/util/gco.m, scripts/plot/util/ginput.m, scripts/plot/util/gnuplot_binary.in, scripts/plot/util/graphics_toolkit.m, scripts/plot/util/hdl2struct.m, scripts/plot/util/hggroup.m, scripts/plot/util/hold.m, scripts/plot/util/isaxes.m, scripts/plot/util/isfigure.m, scripts/plot/util/ishghandle.m, scripts/plot/util/ishold.m, scripts/plot/util/isprop.m, scripts/plot/util/linkprop.m, scripts/plot/util/meshgrid.m, scripts/plot/util/ndgrid.m, scripts/plot/util/newplot.m, scripts/plot/util/print.m, scripts/plot/util/printd.m, scripts/plot/util/private/__add_default_menu__.m, scripts/plot/util/private/__fltk_ginput__.m, scripts/plot/util/private/__fltk_print__.m, scripts/plot/util/private/__ghostscript__.m, scripts/plot/util/private/__gnuplot_get_var__.m, scripts/plot/util/private/__gnuplot_ginput__.m, scripts/plot/util/private/__gnuplot_has_feature__.m, scripts/plot/util/private/__gnuplot_has_terminal__.m, scripts/plot/util/private/__gnuplot_open_stream__.m, scripts/plot/util/private/__gnuplot_print__.m, scripts/plot/util/private/__gnuplot_version__.m, scripts/plot/util/private/__go_draw_axes__.m, scripts/plot/util/private/__go_draw_figure__.m, scripts/plot/util/private/__print_parse_opts__.m, scripts/plot/util/private/__tight_eps_bbox__.m, scripts/plot/util/refresh.m, scripts/plot/util/refreshdata.m, scripts/plot/util/saveas.m, scripts/plot/util/shg.m, scripts/plot/util/struct2hdl.m, scripts/plot/util/subplot.m: Moved from plot to subdir util. * etc/HACKING: Updated directory structure info. * scripts/Makefile.am, scripts/plot/appearance/module.mk, scripts/plot/draw/module.mk, scripts/plot/util/module.mk: Added new directories to build system.
author Rik <rik@octave.org>
date Fri, 04 Oct 2013 17:09:08 -0700
parents scripts/plot/legend.m@225ec9a0222a
children c14e5af64de4
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/legend.m	Fri Oct 04 17:09:08 2013 -0700
@@ -0,0 +1,1595 @@
+## Copyright (C) 2010-2012 David Bateman
+##
+## 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
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {} legend (@var{str1}, @var{str2}, @dots{})
+## @deftypefnx {Function File} {} legend (@var{matstr})
+## @deftypefnx {Function File} {} legend (@var{cellstr})
+## @deftypefnx {Function File} {} legend (@dots{}, "location", @var{pos})
+## @deftypefnx {Function File} {} legend (@dots{}, "orientation", @var{orient})
+## @deftypefnx {Function File} {} legend (@var{hax}, @dots{})
+## @deftypefnx {Function File} {} legend (@var{hobjs}, @dots{})
+## @deftypefnx {Function File} {} legend (@var{hax}, @var{hobjs}, @dots{})
+## @deftypefnx {Function File} {} legend ("@var{option}")
+## @deftypefnx {Function File} {[@var{hleg}, @var{hleg_obj}, @var{hplot}, @var{labels}] =} legend (@dots{})
+##
+## Display a legend for the current axes using the specified strings as labels.
+##
+## Legend entries may be specified as individual character string arguments,
+## a character array, or a cell array of character strings.
+##
+## If the first argument @var{hax} is an axes handle, then plot into this axis,
+## rather than the current axes returned by @code{gca}.  If the handles,
+## @var{hobjs}, are not specified then the legend's strings will be associated
+## with the axes' descendants.  @code{legend} works on line graphs,
+## bar graphs, etc.  A plot must exist before legend is called.
+##
+## The optional parameter @var{pos} specifies the location of the legend
+## as follows:
+##
+## @multitable @columnfractions 0.06 0.14 0.80
+## @headitem @tab pos @tab location of the legend
+## @item @tab north @tab center top
+## @item @tab south @tab center bottom
+## @item @tab east @tab right center
+## @item @tab west @tab left center
+## @item @tab northeast @tab right top (default)
+## @item @tab northwest @tab left top
+## @item @tab southeast @tab right bottom
+## @item @tab southwest @tab left bottom
+## @item
+## @item @tab outside @tab can be appended to any location string
+## @end multitable
+##
+## The optional parameter @var{orient} determines if the key elements
+## are placed vertically or horizontally.  The allowed values are
+## @qcode{"vertical"} (default) or @qcode{"horizontal"}.
+##
+## The following customizations are available using @var{option}:
+##
+## @table @asis
+## @item @qcode{"show"}
+##   Show legend on the plot
+##
+## @item @qcode{"hide"}
+##   Hide legend on the plot
+##
+## @item @qcode{"toggle"}
+##   Toggles between @qcode{"hide"} and @qcode{"show"}
+##
+## @item @qcode{"boxon"}
+##   Show a box around legend (default)
+##
+## @item @qcode{"boxoff"}
+##   Hide the box around legend
+##
+## @item @qcode{"right"}
+##   Place label text to the right of the keys (default)
+##
+## @item @qcode{"left"}
+##   Place label text to the left of the keys
+##
+## @item @qcode{"off"}
+##   Delete the legend object
+## @end table
+##
+## The optional output values are
+##
+## @table @var
+## @item hleg
+##   The graphics handle of the legend object.
+##
+## @item hleg_obj
+##   Graphics handles to the text and line objects which make up the legend.
+##
+## @item hplot
+##   Graphics handles to the plot objects which were used in making the legend.
+##
+## @item labels
+##   A cell array of strings of the labels in the legend.
+## @end table
+##
+## The legend label text is either provided in the call to @code{legend} or
+## is taken from the DisplayName property of graphics objects.  If no
+## labels or DisplayNames are available, then the label text is simply
+## @qcode{"data1"}, @qcode{"data2"}, @dots{}, @nospell{@qcode{"dataN"}}.
+##
+## Implementation Note: A legend is implemented as an additional axes object
+## of the current figure with the @qcode{"tag"} 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)
+
+  if (nargin > 0
+      && (! ishandle (varargin{1})
+          || (strcmp (get (varargin{1}, "type"), "axes")
+              && ! strcmp (get (varargin{1}, "tag"), "legend"))))
+    [ca, varargin, nargin] = __plt_get_axis_arg__ ("legend", varargin{:});
+    if (isempty (ca))
+      ca = gca ();
+    endif
+    fig = get (ca, "parent");
+  else
+    fig = get (0, "currentfigure");
+    if (isempty (fig))
+      fig = gcf ();
+    endif
+    ca = gca ();
+  endif
+
+  ## Special handling for plotyy which has two axes objects
+  if (ishandle (ca) && isprop (ca, "__plotyy_axes__"))
+    plty = get (ca, "__plotyy_axes__");
+    if (isscalar (plty) && ishandle (plty))
+      ca = [ca, plty];
+    elseif (iscell (plty))
+      ca = [ca, plty{:}];
+    elseif (all (ishandle (plty)))
+      ca = [ca, plty(:).'];
+    else
+      error ("legend.m: This should not happen. File a bug report.");
+    endif
+    ## Remove duplicates while preserving order
+    [~, n] = unique (ca);
+    ca = ca(sort (n));
+  endif
+
+  if (nargin > 0 && all (ishandle (varargin{1})))
+    kids = flipud (varargin{1}(:));
+    varargin(1) = [];
+  else
+    kids = ca;
+    kids(strcmp (get (ca, "tag"), "legend")) = [];
+    if (isscalar (kids))
+      kids = get (kids, "children")(:);
+    else
+      kids = flipud ([get(kids, "children"){:}](:));
+    endif
+  endif
+  nargs = numel (varargin);
+  nkids = numel (kids);
+
+  orientation = "default";
+  location = "default";
+  show = "create";
+  textpos = "default";
+  box = "default";
+
+  ## Process old way of specifying location with a number rather than a string.
+  if (nargs > 0)
+    pos = varargin{nargs};
+    if (isnumeric (pos) && isscalar (pos) && pos == fix (pos))
+      if (pos >= -1 && pos <= 4)
+        location = [{"northeastoutside", "best", "northeast",
+                     "northwest", "southwest", "southeast"}] {pos + 2};
+        nargs--;
+      else
+        error ("legend: invalid location specified");
+      endif
+    endif
+  endif
+
+  ## Find location and orientation property/value pairs
+  while (nargs > 1)
+    pos = varargin{nargs-1};
+    str = varargin{nargs};
+    if (strcmpi (pos, "location") && ischar (str))
+      location = lower (str);
+      nargs -= 2;
+    elseif (strcmpi (pos, "orientation") && ischar (str))
+      orientation = lower (str);
+      nargs -= 2;
+    else
+      break;
+    endif
+  endwhile
+
+  ## Validate the orientation
+  switch (orientation)
+    case {"vertical", "horizontal", "default"}
+      ## These are all accepted orientations.
+    otherwise
+      error ("legend: unrecognized legend orientation");
+  endswitch
+
+  ## Validate the location type
+  outside = false;
+  inout = strfind (location, "outside");
+  if (! isempty (inout))
+    outside = true;
+    location = location(1:inout-1);
+  else
+    outside = false;
+  endif
+
+  switch (location)
+    case {"north", "south", "east", "west", "northeast", "northwest", ...
+          "southeast", "southwest", "default"}
+    case "best"
+      warning ("legend: 'best' not yet implemented for location specifier\n");
+      location = "northeast";
+    otherwise
+      error ("legend: unrecognized legend location");
+  endswitch
+
+  ## Find any existing legend object on figure
+  hlegend = [];
+  fkids = get (fig, "children");
+  for i = 1 : numel (fkids)
+    if (   strcmp (get (fkids(i), "type"), "axes")
+        && strcmp (get (fkids(i), "tag"), "legend"))
+      udata = get (fkids(i), "userdata");
+      if (any (udata.handle == ca))
+        hlegend = fkids(i);
+        break;
+      endif
+    endif
+  endfor
+
+  if (nargs == 1)
+    arg = varargin{1};
+    if (ischar (arg))
+      if (rows (arg) == 1)
+        str = tolower (strtrim (arg));
+        switch (str)
+          case "off"
+            delete (hlegend);
+            return;
+          case "hide"
+            show = "off";
+            nargs--;
+          case "show"
+            if (! isempty (hlegend))
+              show = "on";
+            else
+              show = "create";
+              textpos = "right";
+            endif
+            nargs--;
+          case "toggle"
+            if (isempty (hlegend))
+              show = "create";
+              textpos = "right";
+            elseif (strcmp (get (hlegend, "visible"), "off"))
+              show = "on";
+            else
+              show = "off";
+            endif
+            nargs--;
+          case "boxon"
+            box = "on";
+            nargs--;
+          case "boxoff"
+            box = "off";
+            nargs--;
+          case "left"
+            textpos = "left";
+            nargs--;
+          case "right"
+            textpos = "right";
+            nargs--;
+        endswitch
+      else
+        ## Character matrix of labels
+        varargin = cellstr (arg);
+        nargs = numel (varargin);
+      endif
+    elseif (iscellstr (arg))
+      ## Cell array of labels
+      varargin = arg;
+      nargs = numel (varargin);
+    else
+      error ("legend: expecting argument to be a character string");
+    endif
+  elseif (nargs > 1 && iscellstr (varargin{1}))
+    ## Cell array of labels followed by property/value pairs
+    varargin = {varargin{1}{:}, varargin{2:end}};
+    nargs = numel (varargin);
+  endif
+
+  have_labels = (nargs > 0);
+
+  if (strcmp (show, "off"))
+    if (! isempty (hlegend))
+      set (findobj (hlegend), "visible", "off");
+      hlegend = [];
+    endif
+    hobjects = [];
+    hplots  = [];
+    text_strings = {};
+  elseif (strcmp (show, "on"))
+    if (! isempty (hlegend))
+      set (findobj (hlegend), "visible", "on");
+      ## NOTE - Matlab sets both "visible", and "box" to "on"
+      set (hlegend, "visible", get (hlegend, "box"));
+    else
+      hobjects = [];
+      hplots  = [];
+      text_strings = {};
+    endif
+  elseif (strcmp (box, "on"))
+    if (! isempty (hlegend))
+      set (hlegend, "box", "on", "visible", "on");
+    endif
+  elseif (strcmp (box, "off"))
+    if (! isempty (hlegend))
+      set (hlegend, "box", "off", "visible", "off");
+    endif
+  elseif (! have_labels && ! (strcmp (location, "default") &&
+                              strcmp (orientation, "default")))
+    ## Changing location or orientation of existing legend
+    if (! isempty (hlegend))
+      if (strcmp (location, "default"))
+        set (hlegend, "orientation", orientation);
+      elseif (strcmp (orientation, "default"))
+        if (outside)
+          set (hlegend, "location", [location "outside"]);
+        else
+          set (hlegend, "location", location);
+        endif
+      else
+        if (outside)
+          set (hlegend, "location", [location "outside"],
+                        "orientation", orientation);
+        else
+          set (hlegend, "location", location,
+                        "orientation", orientation);
+        endif
+      endif
+    endif
+  else
+    ## Create new legend
+    hobjects = [];
+    hplots  = [];
+    text_strings = {};
+
+    if (have_labels)
+      ## Check for valid data that can be labeled.
+      have_data = false;
+      have_dname = false;
+      for k = 1 : nkids
+        typ = get (kids(k), "type");
+        if (any (strcmp (typ, {"line", "patch", "surface", "hggroup"})))
+          have_data = true;
+          break;
+        endif
+      endfor
+
+      if (! have_data)
+        warning ("legend: plot data is empty; setting key labels has no effect");
+      endif
+    else
+      ## No labels.  Search for DisplayName property.
+      have_dname = false;
+      for k = 1 : nkids
+        hkid = kids(k);
+        typ = get (hkid, "type");
+        if (any (strcmp (typ, {"line", "patch", "surface"})))
+          if (! isempty (get (hkid, "displayname")))
+            have_dname = true;
+            break;
+          endif
+        elseif (strcmp (typ, "hggroup"))
+          hgkids = get (hkid, "children");
+          for j = 1 : length (hgkids)
+            try
+              dname = get (hgkids(j), "DisplayName");
+              if (! isempty (dname))
+                have_dname = true;
+                break;  # break from j-loop over hgkids
+              endif
+            end_try_catch
+          endfor
+          if (have_dname)
+            break;  # break from k loop over nkids
+          endif
+        endif  # elseif hggroup
+      endfor   # for loop k = 1 : nkids
+    endif      # else branch of if (have_labels)
+
+    if (have_labels || ! have_dname)
+      k = nkids;
+      if (! have_labels)
+        varargin = arrayfun (@(x) sprintf ("data%d", x), [1:nkids]',
+                             "uniformoutput", false);
+        have_labels = true;
+        nargs = nkids;
+      endif
+      for i = 1 : nargs
+        arg = varargin{i};
+        if (ischar (arg))
+          typ = get (kids(k), "type");
+          while (k > 0
+                 && ! any (strcmp (typ, {"line","patch","surface","hggroup"})))
+            typ = get (kids(--k), "type");
+          endwhile
+          if (k > 0)
+            if (strcmp (get (kids(k), "type"), "hggroup"))
+              hgkids = get (kids(k), "children");
+              for j = 1 : length (hgkids)
+                hgobj = get (hgkids(j));
+                if (isfield (hgobj, "displayname"))
+                  if (have_labels)
+                    set (hgkids(j), "displayname", arg);
+                  endif
+                  hplots(end+1) = hgkids(j);
+                  text_strings(end+1) = arg;
+                  break;
+                endif
+              endfor
+            else
+              if (have_labels)
+                set (kids(k), "displayname", arg);
+              endif
+              hplots(end+1) = kids(k);
+              text_strings(end+1) = arg;
+            endif
+
+            if (--k == 0)
+              break;
+            endif
+          else
+            break;  # k = 0, no further handles to process
+          endif
+        else
+          error ("legend: expecting argument to be a character string");
+        endif
+      endfor
+      if (have_labels && i < nargs)
+        warning ("legend: ignoring extra labels");
+      endif
+    else
+      ## No labels specified but objects have DisplayName property set.
+      k = nkids;
+      while (k > 0)
+        typ = get (kids(k), "type");
+        while (k > 1
+               && ! any (strcmp (typ, {"line","patch","surface","hggroup"})))
+          typ = get (kids(--k), "type");
+        endwhile
+        if (! any (strcmp (typ, {"line","patch","surface","hggroup"})))
+          break;
+        endif
+        if (k > 0)
+          if (strcmp (get (kids(k), "type"), "hggroup"))
+            hgkids = get (kids(k), "children");
+            for j = 1 : length (hgkids)
+              hgobj = get (hgkids(j));
+              if (isfield (hgobj, "displayname")
+                  && ! isempty (hgobj.displayname))
+                hplots(end+1) = hgkids(j);
+                text_strings(end+1) = hgobj.displayname;
+                break;
+              endif
+            endfor
+          else
+            if (! isempty (get (kids(k), "displayname")))
+              hplots(end+1) = kids(k);
+              text_strings(end+1) = get (kids(k), "displayname");
+            endif
+          endif
+          if (--k == 0)
+            break;
+          endif
+        endif
+      endwhile
+    endif
+
+    if (isempty (hplots))
+      if (! isempty (hlegend))
+        fkids = get (fig, "children");
+        delete (fkids(fkids == hlegend));
+        hlegend = [];
+        hobjects = [];
+        hplots  = [];
+        text_strings = {};
+      endif
+    else
+      ## Preserve the old legend if it exists
+      if (! isempty (hlegend))
+        if (strcmp (textpos, "default"))
+          textpos = get (hlegend, "textposition");
+        endif
+        if (strcmp (location, "default"))
+          location = get (hlegend, "location");
+          inout = strfind (location, "outside");
+          if (! isempty (inout))
+            outside = true;
+            location = location(1:inout-1);
+          else
+            outside = false;
+          endif
+        endif
+        if (strcmp (orientation, "default"))
+          orientation = get (hlegend, "orientation");
+        endif
+        box = get (hlegend, "box");
+      else
+        if (strcmp (textpos, "default"))
+          textpos = "right";
+        endif
+        if (strcmp (location, "default"))
+          location = "northeast";
+        endif
+        if (strcmp (orientation, "default"))
+          orientation = "vertical";
+        endif
+        box = "on";
+      endif
+
+      ## Get axis size and fontsize in points.
+      ## Rely on listener to handle coversion.
+      units = get (ca(1), "units");
+      unwind_protect
+        set (ca(1), "units", "points");
+        set (ca(1), "fontunits", "points");
+        if (isempty (hlegend) || ! isprop (hlegend, "unmodified_axes_position"))
+          unmodified_axes_position = get (ca(1), "position");
+          unmodified_axes_outerposition = get (ca(1), "outerposition");
+        else
+          unmodified_axes_position = get (hlegend, "unmodified_axes_position");
+          unmodified_axes_outerposition = get (hlegend, ...
+                                               "unmodified_axes_outerposition");
+        endif
+        ca_pos = unmodified_axes_position;
+        ca_outpos = unmodified_axes_outerposition;
+        tightinset = get (ca(1), "tightinset");
+        for i = 2 : numel (ca)
+          tightinset = max (tightinset, get (ca(i), "tightinset"));
+        endfor
+      unwind_protect_cleanup
+        set (ca(1), "units", units);
+      end_unwind_protect
+
+      ## Padding between legend entries horizontally and vertically
+      xpad = 2;
+      ypad = 2;
+
+      linelength = 15;
+
+      ## Create the axis first
+      curaxes = get (fig, "currentaxes");
+      unwind_protect
+        ud = ancestor (hplots, "axes");
+        if (! isscalar (ud))
+          ud = unique ([ud{:}]);
+        endif
+        if (isempty (hlegend))
+          addprops = true;
+          hlegend = axes ("tag", "legend", "userdata", struct ("handle", ud),
+                          "box", box,
+                          "xtick", [], "ytick", [],
+                          "xlim", [0, 1], "ylim", [0, 1],
+                          "visible", ifelse (strcmp (box, "on"), "on", "off"),
+                          "activepositionproperty", "position",
+                          "interpreter", "tex");
+          ## Inherit properties from current axis
+          ## "fontunits" shoud be first because it affects interpretation
+          ## of "fontsize" property
+          proplist = {"fontunits", "fontangle", "fontname", "fontsize", ...
+                      "fontweight"};
+          ca_props = get (ca(1), proplist); 
+          set (hlegend, proplist, ca_props);
+        else
+          addprops = false;
+          axes (hlegend);
+          delete (get (hlegend, "children"));
+        endif
+        if (addprops)
+          addproperty ("edgecolor", hlegend, "color", [0, 0, 0]);
+          addproperty ("textcolor", hlegend, "color", [0, 0, 0]);
+          locations = {"north", "south", "east", "west", ...
+                       "{northeast}", "southeast", "northwest", "southwest", ...
+                       "northoutside", "southoutside", ...
+                       "eastoutside", "westoutside", ...
+                       "northeastoutside", "southeastoutside", ...
+                       "northwestoutside", "southwestoutside"};
+          addproperty ("location", hlegend, "radio", strjoin (locations, "|"));
+          addproperty ("orientation", hlegend, "radio",
+                       "{vertical}|horizontal");
+          addproperty ("string", hlegend, "any", text_strings);
+          addproperty ("textposition", hlegend, "radio", "left|{right}");
+        endif
+        ## Inherit visual properties from legend object
+        fontunits = get (hlegend, "fontunits");
+        fontangle = get (hlegend, "fontangle");
+        fontname = get (hlegend, "fontname");
+        fontsize = get (hlegend, "fontsize");
+        fontweight = get (hlegend, "fontweight");
+        interpreter = get (hlegend, "interpreter");
+        textcolor = get (hlegend, "textcolor");
+        ## Add text label to the axis first, checking their extents
+        nentries = numel (hplots);
+        texthandle = [];
+        maxwidth = 0;
+        maxheight = 0;
+        for k = 1 : nentries
+          halign = ifelse (strcmp (textpos, "right"), "left", "right");
+          texthandle(end+1) = text (0, 0, text_strings{k},
+                                    "color", textcolor,
+                                    "horizontalalignment", halign,
+                                    "interpreter", interpreter,
+                                    "fontunits", fontunits,
+                                    "fontangle", fontangle,
+                                    "fontname", fontname,
+                                    "fontsize", fontsize,
+                                    "fontweight", fontweight,
+                                    "userdata", hplots(k));
+          units = get (texthandle(end), "units");
+          unwind_protect
+            set (texthandle(end), "units", "points");
+            extents = get (texthandle(end), "extent");
+            maxwidth = max (maxwidth, extents(3));
+            maxheight = max (maxheight, extents(4));
+          unwind_protect_cleanup
+            set (texthandle(end), "units", units);
+          end_unwind_protect
+        endfor
+
+        num1 = nentries;
+        if (strcmp (orientation, "vertical"))
+          height = nentries * (ypad + maxheight);
+          if (outside)
+            if (height > ca_pos(4))
+              ## Avoid shrinking the height of the axis to zero if outside
+              num1 = ca_pos(4) / (maxheight + ypad) / 2;
+            endif
+          else
+            if (height > 0.9 * ca_pos(4))
+              num1 = 0.9 * ca_pos(4) / (maxheight + ypad);
+            endif
+          endif
+        else
+          width = nentries * (ypad + maxwidth);
+          if (outside)
+            if (width > ca_pos(3))
+              ## Avoid shrinking the width of the axis to zero if outside
+              num1 = ca_pos(3) / (maxwidth + ypad) / 2;
+            endif
+          else
+            if (width > 0.9 * ca_pos(3))
+              num1 = 0.9 * ca_pos(3) / (maxwidth + ypad);
+            endif
+          endif
+        endif
+        num2 = ceil (nentries / num1);
+
+        xstep = 3 * xpad + (maxwidth + linelength);
+        if (strcmp (textpos, "right"))
+          xoffset = xpad;
+          txoffset = 2 * xpad + linelength;
+        else
+          xoffset = 2 * xpad + maxwidth;
+          txoffset = xpad + maxwidth;
+        endif
+        ystep = (ypad + maxheight);
+        yoffset = ystep / 2;
+
+        ## Place the legend in the desired location
+        if (strcmp (orientation, "vertical"))
+          lpos = [0, 0, num2 * xstep, num1 * ystep];
+        else
+          lpos = [0, 0, num1 * xstep, num2 * ystep];
+        endif
+
+        gnuplot = strcmp (get (fig, "__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
+          ## position.
+          if (isempty (strfind (location, "east")))
+            gnuplot_offset = unmodified_axes_outerposition(1) ...
+                           + unmodified_axes_outerposition(3) ...
+                           - unmodified_axes_position(1) ...
+                           - unmodified_axes_position(3);
+          else
+            gnuplot_offset = unmodified_axes_position(1) ...
+                           - unmodified_axes_outerposition(1);
+          endif
+          ## FIXME: The "fontsize" is added to match the behavior of OpenGL.
+          ## This implies that a change in fontsize should trigger a listener
+          ## to update the legend.  The "2" was determined using a long legend
+          ## key in the absence of any subplots.
+          gnuplot_offset = gnuplot_offset - 2 * fontsize;
+        else
+          gnuplot_offset = 0;
+        endif
+
+        ## For legend's outside the associated axes postion,
+        ## align their edge to the unmodified_axes_outerpostion,
+        ## and adjust the axes postion accordingly.
+        switch (location)
+          case "north"
+            if (outside)
+              lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
+                      ca_outpos(2) + ca_outpos(4) - lpos(4) - ypad, lpos(3), ...
+                      lpos(4)];
+
+              new_pos = [ca_pos(1), ca_pos(2), ca_pos(3), ca_pos(4) - lpos(4)];
+            else
+              lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
+                      ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), lpos(4)];
+            endif
+          case "south"
+            if (outside)
+              lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
+                      ca_outpos(2) + ypad, lpos(3), lpos(4)];
+              new_pos = [ca_pos(1), lpos(2) + lpos(4) + 2 * ypad ...
+                      + tightinset(2), ca_pos(3), ...
+                         ca_pos(4) - lpos(4)];
+            else
+              lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
+                      ca_pos(2) + ypad, lpos(3), lpos(4)];
+            endif
+          case "east"
+            if (outside)
+              lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3) - ypad, ...
+                      ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
+              new_pos = [ca_pos(1), ca_pos(2), ...
+                         lpos(1) - 2 * xpad - ca_pos(1) - tightinset(3), ...
+                         ca_pos(4)];
+              new_pos(3) = new_pos(3) + gnuplot_offset;
+            else
+              lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
+                      ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
+            endif
+          case "west"
+            if (outside)
+              lpos = [ca_outpos(1) + ypad, ...
+                      ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, ...
+                      lpos(3), lpos(4)];
+              new_pos = [lpos(1) + lpos(3) + 2 * xpad + tightinset(1), ...
+                         ca_pos(2), ca_pos(3) - lpos(3) - 2 * xpad, ca_pos(4)];
+              new_pos(1) = new_pos(1) - gnuplot_offset;
+              new_pos(3) = new_pos(3) + gnuplot_offset;
+            else
+              lpos = [ca_pos(1) +  ypad, ...
+                      ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
+            endif
+          case "northeast"
+            if (outside)
+              lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3) - ypad, ...
+                      ca_pos(2) + ca_pos(4) - lpos(4), lpos(3), lpos(4)];
+              new_pos = [ca_pos(1), ca_pos(2), ...
+                         lpos(1) - 2 * xpad - tightinset(3) - ca_pos(1), ...
+                         ca_pos(4)];
+              new_pos(3) = new_pos(3) + gnuplot_offset;
+            else
+              lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
+                      ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), lpos(4)];
+            endif
+          case "northwest"
+            if (outside)
+              lpos = [ca_outpos(1) + ypad , ca_pos(2) + ca_pos(4) - lpos(4), ...
+                      lpos(3), lpos(4)];
+              new_pos = [lpos(1) + lpos(3) + 2 * xpad + tightinset(1), ...
+              ca_pos(2), ca_pos(3) - lpos(3) - 2 * xpad, ca_pos(4)];
+              new_pos(1) = new_pos(1) - gnuplot_offset;
+              new_pos(3) = new_pos(3) + gnuplot_offset;
+            else
+              lpos = [ca_pos(1) + ypad, ...
+                      ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), lpos(4)];
+            endif
+          case "southeast"
+            if (outside)
+              lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3) - ypad, ...
+                      ca_pos(2), lpos(3), lpos(4)];
+              new_pos = [ca_pos(1), ca_pos(2), ...
+                         lpos(1) - 2 * xpad - ca_pos(1) - tightinset(3), ...
+                         ca_pos(4)];
+              new_pos(3) = new_pos(3) + gnuplot_offset;
+            else
+              lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
+                      ca_pos(2) + ypad, lpos(3), lpos(4)];
+            endif
+          case "southwest"
+            if (outside)
+              lpos = [ca_outpos(1) + ypad, ca_pos(2), lpos(3), lpos(4)];
+              new_pos = [lpos(1) + lpos(3) + 2 * xpad + tightinset(1), ...
+              ca_pos(2), ca_pos(3) - lpos(3) - 2 * xpad, ca_pos(4)];
+              new_pos(1) = new_pos(1) - gnuplot_offset;
+              new_pos(3) = new_pos(3) + gnuplot_offset;
+            else
+              lpos = [ca_pos(1) + ypad, ca_pos(2) + ypad, lpos(3), lpos(4)];
+            endif
+        endswitch
+
+        units = get (hlegend, "units");
+        unwind_protect
+          set (hlegend, "units", "points");
+          set (hlegend, "position", lpos);
+        unwind_protect_cleanup
+          set (hlegend, "units", units);
+        end_unwind_protect
+
+        ## Now write the line segments and place the text objects correctly
+        xk = 0;
+        yk = 0;
+        for k = 1 : numel (hplots)
+          hobjects(end+1) = texthandle(k);
+          switch (get (hplots(k), "type"))
+
+            case "line"
+              color = get (hplots(k), "color");
+              style = get (hplots(k), "linestyle");
+              if (! strcmp (style, "none"))
+                l1 = line ("xdata", ([xoffset, xoffset + linelength] + xk * xstep) / lpos(3),
+                           "ydata", [1, 1] .* (lpos(4) - yoffset - yk * ystep) / lpos(4),
+                           "color", color, "linestyle", style,
+                           "marker", "none",
+                           "userdata", hplots(k));
+                hobjects(end+1) = l1;
+              endif
+              marker = get (hplots(k), "marker");
+              if (! strcmp (marker, "none"))
+                l1 = line ("xdata", (xoffset + 0.5 * linelength  + xk * xstep) / lpos(3),
+                           "ydata", (lpos(4) - yoffset - yk * ystep) / lpos(4),
+                           "color", color, "linestyle", "none",
+                           "marker", marker,
+                           "markeredgecolor",get (hplots(k), "markeredgecolor"),
+                           "markerfacecolor",get (hplots(k), "markerfacecolor"),
+                           "markersize", get (hplots(k), "markersize"),
+                           "userdata", hplots(k));
+                hobjects(end+1) = l1;
+              endif
+
+              if (addprops)
+                addlistener (hplots(k), "color",
+                             {@updateline, hlegend, linelength, false});
+                addlistener (hplots(k), "linestyle",
+                             {@updateline, hlegend, linelength, false});
+                addlistener (hplots(k), "marker",
+                             {@updateline, hlegend, linelength, false});
+                addlistener (hplots(k), "markeredgecolor",
+                             {@updateline, hlegend, linelength, false});
+                addlistener (hplots(k), "markerfacecolor",
+                             {@updateline, hlegend, linelength, false});
+                addlistener (hplots(k), "markersize",
+                             {@updateline, hlegend, linelength, false});
+                addlistener (hplots(k), "displayname",
+                             {@updateline, hlegend, linelength, true});
+              endif
+
+            case "patch"
+              facecolor = get (hplots(k), "facecolor");
+              edgecolor = get (hplots(k), "edgecolor");
+              cdata = get (hplots(k), "cdata");
+              if (! strcmp (facecolor, "none") || ! strcmp (edgecolor, "none"))
+                p1 = patch ("xdata", ([0, linelength, linelength, 0] +
+                                      xoffset + xk * xstep) / lpos(3),
+                            "ydata", (lpos(4) - yoffset -
+                                      [yk-0.3, yk-0.3, yk+0.3, yk+0.3] .* ystep) / lpos(4),
+                           "facecolor", facecolor, "edgecolor", edgecolor,
+                           "cdata", cdata, "userdata", hplots(k));
+                hobjects(end+1) = p1;
+              endif
+              ## FIXME: Probably need listeners, as for line objects
+
+            case "surface"
+              facecolor = get (hplots(k), "facecolor");
+              edgecolor = get (hplots(k), "edgecolor");
+              cdata = sum (get (ca(1), "clim")) / 2;
+              if (! strcmp (facecolor, "none") || ! strcmp (edgecolor, "none"))
+                p1 = patch ("xdata", ([0, linelength, linelength, 0] +
+                                      xoffset + xk * xstep) / lpos(3),
+                            "ydata", (lpos(4) - yoffset -
+                                      [yk-0.3, yk-0.3, yk+0.3, yk+0.3] .* ystep) / lpos(4),
+                           "facecolor", facecolor, "edgecolor", edgecolor,
+                           "cdata", cdata, "userdata", hplots(k));
+                hobjects(end+1) = p1;
+              endif
+              ## FIXME: Probably need listeners, as for line objects
+
+          endswitch
+
+          set (texthandle(k), "position",
+                              [(txoffset + xk * xstep) / lpos(3), ...
+                               (lpos(4) - yoffset - yk * ystep) / lpos(4)]);
+          if (strcmp (orientation, "vertical"))
+            yk++;
+            if (yk > num1)
+              yk = 0;
+              xk++;
+            endif
+          else
+            xk++;
+            if (xk > num1)
+              xk = 0;
+              yk++;
+            endif
+          endif
+        endfor
+
+        ## Add an invisible text object to original axis
+        ## that when it is destroyed will remove the legend
+        props = {"parent", ca(1), "tag", "legend", ...
+                 "handlevisibility", "off", "visible", "off", ...
+                 "xliminclude", "off", "yliminclude", "off"};
+        t1 = findall (ca(1), "tag", "legend", "type", "text");
+        if (isempty (t1))
+          t1 = text (0, 0, "", props{:});
+          set (t1, "deletefcn", {@deletelegend1, hlegend});
+        endif
+        if (isprop (hlegend, "unmodified_axes_position"))
+          set (hlegend, "unmodified_axes_position",
+                         unmodified_axes_position,
+                        "unmodified_axes_outerposition",
+                         unmodified_axes_outerposition);
+        else
+          addproperty ("unmodified_axes_position", hlegend,
+                       "data", unmodified_axes_position);
+          addproperty ("unmodified_axes_outerposition", hlegend,
+                       "data", unmodified_axes_outerposition);
+        endif
+
+        ## Resize the axis that the legend is attached to if the legend is
+        ## "outside" the plot and create a listener to resize axis to original
+        ## size if the legend is deleted, hidden, or shown.
+        if (outside)
+          for i = 1 : numel (ca)
+            units = get (ca(i), "units");
+            unwind_protect
+              set (ca(i), "units", "points");
+              if (gnuplot && numel (ca) == 1)
+                ## Let Gnuplot handle the positioning of the keybox.
+                ## This violates strict Matlab compatibility, but reliably
+                ## renders an esthetic result.
+                set (ca(i), "position",  unmodified_axes_position);
+                set (ca(i), "activepositionproperty", "outerposition")
+              else
+                ## numel (ca) > 1 for axes overlays (like plotyy)
+                set (ca(i), "position", new_pos);
+              endif
+            unwind_protect_cleanup
+              set (ca(i), "units", units);
+            end_unwind_protect
+          endfor
+
+          set (hlegend, "deletefcn", {@deletelegend2, ca, ...
+                                      unmodified_axes_position, ...
+                                      unmodified_axes_outerposition, ...
+                                      t1, hplots});
+          addlistener (hlegend, "visible", {@hideshowlegend, ca, ...
+                                            unmodified_axes_position, ...
+                                            new_pos});
+        else
+          set (hlegend, "deletefcn", {@deletelegend2, ca, [], [], t1, hplots});
+        endif
+
+        if (! addprops)
+          ## Remove listeners on existing legend temporarily to stop recursion.
+          dellistener (hlegend, "location");
+          dellistener (hlegend, "orientation");
+          dellistener (hlegend, "string");
+          dellistener (hlegend, "textposition");
+        endif
+
+        if (! addprops)
+          set (hlegend, "string", text_strings);
+        endif
+
+        if (outside)
+          set (hlegend, "location", [location "outside"],
+                        "orientation", orientation, "textposition", textpos);
+        else
+          set (hlegend, "location", location, "orientation", orientation,
+                        "textposition", textpos);
+        endif
+
+        if (addprops)
+          addlistener (hlegend, "edgecolor", @updatelegendtext);
+          addlistener (hlegend, "fontangle", @updatelegendtext);
+          addlistener (hlegend, "fontname", @updatelegendtext);
+          addlistener (hlegend, "fontweight", @updatelegendtext);
+          addlistener (hlegend, "textcolor", @updatelegendtext);
+          ## Properties which could change size of box, such as fontsize,
+          ## require legend to be redrawn.
+          addlistener (hlegend, "fontsize", @updatelegend);
+          addlistener (hlegend, "fontunits", @updatelegend);
+          addlistener (hlegend, "interpreter", @updatelegend);
+          addlistener (hlegend, "location", @updatelegend);
+          addlistener (hlegend, "orientation", @updatelegend);
+          addlistener (hlegend, "string", @updatelegend);
+          addlistener (hlegend, "textposition", @updatelegend);
+          ## FIXME: need to add listeners for tightinset and position
+          ##        addlistener (ca, "tightinset", @update????);
+          ##        addlistener (ca, "position", @update????);
+        else
+          ## Restore certain listeners
+          addlistener (hlegend, "location", @updatelegend);
+          addlistener (hlegend, "orientation", @updatelegend);
+          addlistener (hlegend, "string", @updatelegend);
+          addlistener (hlegend, "textposition", @updatelegend);
+        endif
+      unwind_protect_cleanup
+        set (fig, "currentaxes", curaxes);
+      end_unwind_protect
+    endif
+  endif
+
+  if (nargout > 0)
+    hleg = hlegend;
+    hleg_obj = hobjects;
+    hplot = hplots;
+    labels = text_strings;
+  endif
+
+endfunction
+
+function updatelegend (h, ~)
+  persistent recursive = false;
+
+  if (! recursive)
+    recursive = true;
+    unwind_protect
+      hax = getfield (get (h, "userdata"), "handle");
+      [hplots, ~] = __getlegenddata__ (h);
+      position = get (h, "unmodified_axes_position");
+      outerposition = get (h, "unmodified_axes_outerposition");
+      units = get (hax, "units");
+      set (hax, "units", "points");
+      switch (get (hax, "activepositionproperty"))
+        case "position"
+          set (hax, "outerposition", outerposition);
+          set (hax, "position", position);
+        case "outerposition"
+          set (hax, "position", position);
+          set (hax, "outerposition", outerposition);
+      endswitch
+      set (hax, "units", units);
+      h = legend (hax, hplots, get (h, "string"));
+    unwind_protect_cleanup
+      recursive = false;
+    end_unwind_protect
+  endif
+
+endfunction
+
+function updatelegendtext (h, ~)
+  kids = get (h, "children");
+  htext = [];
+  for i = 1:numel (kids)
+    if (strcmp (get (kids(i), "type"), "text"))
+      htext(end+1) = kids(i);
+    endif
+  endfor
+
+  tprops = {"fontangle", "fontname", "fontweight", "color"};
+  lprops = {"fontangle", "fontname", "fontweight", "textcolor"};
+  set (htext, tprops, get (h, lprops));
+
+  ec = get (h, "edgecolor");
+  set (h, "xcolor", ec, "ycolor", ec);
+endfunction
+
+function hideshowlegend (h, ~, ca, pos1, pos2)
+  isvisible = strcmp (get (h, "visible"), "off");
+  if (! isvisible)
+    kids = get (h, "children");
+    if (any (! strcmp (get (kids, "visible"), "off")))
+      isvisible = true;
+    endif
+  endif
+
+  for i = 1 : numel (ca)
+    if (isaxes (ca(i))
+        && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"),"off"))
+        && strcmp (get (ca(i), "beingdeleted"), "off"))
+      units = get (ca(i), "units");
+      unwind_protect
+        set (ca(i), "units", "points");
+        if (isvisible)
+          set (ca(i), "position", pos2);
+        else
+          set (ca(i), "position", pos1);
+        endif
+      unwind_protect_cleanup
+        set (ca(i), "units", units);
+      end_unwind_protect
+    endif
+  endfor
+endfunction
+
+function deletelegend1 (h, ~, ca)
+  if (isaxes (ca)
+      && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"), "off"))
+      && strcmp (get (ca, "beingdeleted"), "off"))
+    delete (ca);
+  endif
+endfunction
+
+function deletelegend2 (h, ~, ca, pos, outpos, t1, hplots)
+  for i = 1 : numel (ca)
+    if (isaxes (ca(i))
+        && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"), "off"))
+        && strcmp (get (ca(i), "beingdeleted"), "off"))
+      if (! isempty (pos) && ! isempty (outpos))
+        units = get (ca(i), "units");
+        unwind_protect
+          set (ca(i), "units", "points");
+          set (ca(i), "position", pos, "deletefcn", "");
+        unwind_protect_cleanup
+          set (ca(i), "units", units);
+        end_unwind_protect
+      endif
+    endif
+  endfor
+  set (t1, "deletefcn", "");
+  delete (t1);
+  for i = 1 : numel (hplots)
+    if (ishandle (hplots(i)) && strcmp (get (hplots(i), "type"), "line"))
+      dellistener (hplots(i), "color");
+      dellistener (hplots(i), "linestyle");
+      dellistener (hplots(i), "marker");
+      dellistener (hplots(i), "markeredgecolor");
+      dellistener (hplots(i), "markerfacecolor");
+      dellistener (hplots(i), "markersize");
+      dellistener (hplots(i), "displayname");
+    endif
+  endfor
+endfunction
+
+function updateline (h, ~, hlegend, linelength, update_name)
+
+  if (update_name)
+    ## When string changes, have to rebuild legend completely
+    [hplots, text_strings] = __getlegenddata__ (hlegend);
+    legend (hplots, text_strings);
+  else
+    kids = get (hlegend, "children");
+    ll = lm = [];
+    for i = 1 : numel (kids)
+      if (get (kids(i), "userdata") == h
+          && strcmp (get (kids(i), "type"), "line"))
+        if (strcmp (get (kids(i), "marker"), "none"))
+          ll = kids(i);
+        else
+          lm = kids(i);
+        endif
+      endif
+    endfor
+
+    [linestyle, marker, displayname] = ...
+      get (h, {"linestyle", "marker", "displayname"}){:};
+
+    if (! isempty (ll))
+      [xpos1, ypos1] = get (ll, {"xdata", "ydata"}){:};
+      xpos2 = sum (xpos1) / 2;
+      ypos2 = ypos1(1);
+      delete (ll);
+      if (! isempty (lm))
+        delete (lm);
+      endif
+    else
+      [xpos2, ypos2] = get (lm, {"xdata", "ydata"}){:};
+      xpos1 = xpos2 + [-0.5, 0.5] * linelength;
+      ypos1 = [ypos2, ypos2];
+      delete (lm);
+    endif
+
+    if (! strcmp (linestyle, "none"))
+      line ("xdata", xpos1, "ydata", ypos1, "color", get (h, "color"),
+            "linestyle", get (h, "linestyle"), "marker", "none",
+            "userdata", h, "parent", hlegend);
+    endif
+    if (! strcmp (marker, "none"))
+      line ("xdata", xpos2, "ydata", ypos2, "color", get (h, "color"),
+            "marker", marker, "markeredgecolor", get (h, "markeredgecolor"),
+            "markerfacecolor", get (h, "markerfacecolor"),
+            "markersize", get (h, "markersize"), "linestyle", "none",
+            "userdata", h, "parent", hlegend);
+    endif
+  endif
+endfunction
+
+
+%!demo
+%! clf;
+%! plot (rand (2));
+%! title ('legend called with cellstr and string inputs for labels');
+%! h = legend ({'foo'}, 'bar');
+%! legend location northeastoutside
+%! set (h, 'fontsize', 20);
+
+%!demo
+%! clf;
+%! plot (rand (3));
+%! title ('legend() without inputs creates default labels');
+%! h = legend ();
+
+%!demo
+%! clf;
+%! x = 0:1;
+%! plot (x,x,';I am Blue;', x,2*x, x,3*x,';I am Red;');
+%! legend location northeastoutside
+%! ## Placing legend inside should return axes to original size
+%! legend location northeast
+%! title ('Blue and Red keys, with Green missing');
+
+%!demo
+%! clf;
+%! plot (1:10, 1:10, 1:10, fliplr (1:10));
+%! title ('incline is blue and decline is green');
+%! legend ({'I am blue', 'I am green'}, 'location', 'east');
+%! legend hide
+%! legend show
+
+%!demo
+%! clf;
+%! plot (1:10, 1:10, 1:10, fliplr (1:10));
+%! title ('Legend with keys in horizontal orientation');
+%! legend ({'I am blue', 'I am green'}, ...
+%!         'location', 'east', 'orientation', 'horizontal');
+%! legend boxoff
+%! legend boxon
+
+%!demo
+%! clf;
+%! plot (1:10, 1:10, 1:10, fliplr (1:10));
+%! title ('Legend with box off');
+%! legend ({'I am blue', 'I am green'}, 'location', 'east');
+%! legend boxoff
+
+%!demo
+%! clf;
+%! plot (1:10, 1:10, 1:10, fliplr (1:10));
+%! title ('Legend with text to the left of key');
+%! legend ({'I am blue', 'I am green'}, 'location', 'east');
+%! legend left
+
+%!demo
+%! clf;
+%! plot (1:10, 1:10, 1:10, fliplr (1:10));
+%! title ({'Use properties to place legend text to the left of key', ...
+%!         'Legend text color is magenta'});
+%! h = legend ({'I am blue', 'I am green'}, 'location', 'east');
+%! legend ('right');
+%! set (h, 'textposition', 'left');
+%! set (h, 'textcolor', [1 0 1]);
+
+%!demo
+%! clf;
+%! plot (1:10, 1:10, 1:10, fliplr (1:10));
+%! title ('Legend is hidden')
+%! legend ({'I am blue', 'I am green'}, 'location', 'east');
+%! legend hide
+
+%!demo
+%! clf;
+%! x = 0:1;
+%! plot (x,x,';I am Blue;', x,2*x,';I am Green;', x,3*x,';I am Red;');
+%! title ({'Labels are embedded in call to plot', ...
+%!         'Legend is hidden and then shown'});
+%! legend boxon
+%! legend hide
+%! legend show
+
+%!demo
+%! clf;
+%! x = 0:1;
+%! plot (x, x, ';\alpha;',  ...
+%!       x, 2*x, ';\beta=2\alpha;',  ...
+%!       x, 3*x, ';\gamma=3\alpha;');
+%! h = legend ();
+%! set (h, 'interpreter', 'tex');
+%! title ('Labels with interpreted Greek text');
+
+%!demo
+%! clf;
+%! plot (rand (2));
+%! title ('Labels with TeX interpreter turned off');
+%! h = legend ('Hello_World', 'foo^bar');
+%! set (h, 'interpreter', 'none');
+
+%!demo
+%! clf;
+%! plot (1:10, 1:10);
+%! title ('a very long label can sometimes cause problems');
+%! legend ('hello very big world', 'location', 'northeastoutside');
+
+%!demo
+%! clf;
+%! labels = {};
+%! colororder = get (gca, 'colororder');
+%! for i = 1:5
+%!   h = plot (1:100, i + rand (100,1)); hold on;
+%!   set (h, 'color', colororder(i,:));
+%!   labels = {labels{:}, ['Signal ', num2str(i)]};
+%! end
+%! hold off;
+%! title ({'Signals with random offset and uniform noise';
+%!         'Legend shown below and outside of plot'});
+%! xlabel ('Sample Nr [k]'); ylabel ('Amplitude [V]');
+%! legend (labels, 'location', 'southoutside');
+
+%!demo
+%! clf;
+%! x = linspace (0, 10);
+%! plot (x, x);
+%! hold on;
+%! stem (x, x.^2, 'g');
+%! title ('First created object gets first label');
+%! legend ('linear');
+%! hold off;
+
+%!demo
+%! clf;
+%! x = linspace (0, 10);
+%! plot (x, x, x, x.^2);
+%! title ('First created object gets first label');
+%! legend ('linear');
+
+%!demo
+%! clf;
+%! x = linspace (0, 10);
+%! plot (x, x, x, x.^2);
+%! title ('Labels are applied in order of object creation');
+%! legend ('linear', 'quadratic');
+
+%!demo
+%! clf;
+%! rand_2x3_data1 = [0.341447, 0.171220, 0.284370; 0.039773, 0.731725, 0.779382];
+%! bar (rand_2x3_data1);
+%! ylim ([0 1.0]);
+%! title ('legend() works for bar graphs (hggroups)');
+%! legend ({'1st Bar', '2nd Bar', '3rd Bar'});
+
+%!demo
+%! clf;
+%! rand_2x3_data2 = [0.44804, 0.84368, 0.23012; 0.72311, 0.58335, 0.90531];
+%! bar (rand_2x3_data2);
+%! ylim ([0 1.2]);
+%! title ('legend() works for bar graphs (hggroups)');
+%! legend ('1st Bar', '2nd Bar', '3rd Bar');
+%! legend right;
+
+%!demo
+%! clf;
+%! x = 0:0.1:7;
+%! h = plot (x,sin(x), x,cos(x), x,sin(x.^2/10), x,cos(x.^2/10));
+%! title ('Only the sin() objects have keylabels');
+%! legend (h([1, 3]), {'sin (x)', 'sin (x^2/10)'}, 'location', 'southwest');
+
+%!demo
+%! clf;
+%! x = 0:0.1:10;
+%! plot (x, sin (x), ';sin (x);');
+%! hold all;
+%! plot (x, cos (x), ';cos (x);');
+%! hold off;
+%! title ('legend constructed from multiple plot calls');
+
+%!demo
+%! clf;
+%! x = 0:0.1:10;
+%! plot (x, sin (x), ';sin (x);');
+%! hold all;
+%! plot (x, cos (x), ';cos (x);');
+%! hold off;
+%! title ('Specified label text overrides previous labels');
+%! legend ({'Sine', 'Cosine'}, 'location', 'northeastoutside');
+
+%!demo
+%! clf;
+%! x = 0:10;
+%! plot (x, rand (11));
+%! xlabel ('Indices');
+%! ylabel ('Random Values');
+%! title ('Legend ''off'' deletes the legend');
+%! legend (cellstr (num2str ((1:10)')), 'location', 'northeastoutside');
+%! legend off;
+%! axis ([0, 10, 0 1]);
+
+%!demo
+%! clf;
+%! x = (1:5)';
+%! subplot (2,2,1);
+%!  plot (x, rand (numel (x)));
+%!  legend (cellstr (num2str (x)), 'location', 'northwestoutside');
+%! subplot (2,2,2);
+%!  plot (x, rand (numel (x)));
+%!  legend (cellstr (num2str (x)), 'location', 'northeastoutside');
+%! subplot (2,2,3);
+%!  plot (x, rand (numel (x)));
+%!  legend (cellstr (num2str (x)), 'location', 'southwestoutside');
+%! subplot (2,2,4);
+%!  plot (x, rand (numel (x)));
+%!  legend (cellstr (num2str (x)), 'location', 'southeastoutside');
+
+%!demo
+%! clf;
+%! plot (rand (2));
+%! title ('legend() will warn if extra labels are specified');
+%! legend ('Hello', 'World', 'interpreter', 'foobar');
+
+%!demo
+%! clf;
+%! x = 0:10;
+%! y1 = rand (size (x));
+%! y2 = rand (size (x));
+%! [ax, h1, h2] = plotyy (x, y1, x, y2);
+%! title ('plotyy legend test #1: Blue and Green labels');
+%! legend ([h1, h2], {'Blue', 'Green'}, 'location', 'south');
+
+%!demo
+%! clf;
+%! x = 0:10;
+%! y1 = rand (size (x));
+%! y2 = rand (size (x));
+%! [ax, h1, h2] = plotyy (x, y1, x, y2);
+%! title ('plotyy legend test #2: Blue and Green labels');
+%! legend ({'Blue', 'Green'}, 'location', 'south');
+
+%!demo
+%! clf;
+%! x = 0:10;
+%! y1 = rand (size (x));
+%! y2 = rand (size (x));
+%! [ax, h1, h2] = plotyy (x, y1, x, y2);
+%! title ('plotyy legend test #3: Blue and Green labels');
+%! legend ('Blue', 'Green', 'location', 'south');
+
+%!demo % bug 36408
+%! clf;
+%! option = 'right';
+%! subplot (3,1,1);
+%!  plot (rand (1,4));
+%!  xlabel xlabel;
+%!  ylabel ylabel;
+%!  title ('Subplots should adjust to the legend placed outside');
+%!  legend ({'1'}, 'location', 'northeastoutside');
+%!  legend (option);
+%! subplot (3,1,2);
+%!  plot (rand (1,4));
+%!  xlabel xlabel;
+%!  ylabel ylabel;
+%!  legend ({'1234567890'}, 'location', 'eastoutside');
+%!  legend (option);
+%! subplot (3,1,3);
+%!  plot (rand (1,4));
+%!  xlabel xlabel;
+%!  ylabel ylabel;
+%!  legend ({'12345678901234567890'}, 'location', 'southeastoutside');
+%!  legend (option);
+
+%!demo % bug 36408
+%! clf;
+%! option = 'right';
+%! subplot (3,1,1);
+%!  plot (rand (1,4));
+%!  title ('Subplots should adjust to the legend placed outside');
+%!  legend ({'1'}, 'location', 'northwestoutside');
+%!  legend (option);
+%! subplot (3,1,2);
+%!  plot (rand (1,4));
+%!  legend ({'1234567890'}, 'location', 'westoutside');
+%!  legend (option);
+%! subplot (3,1,3);
+%!  plot (rand (1,4));
+%!  legend ({'12345678901234567890'}, 'location', 'southwestoutside');
+%!  legend (option);
+
+%!demo % bug 36408
+%! clf;
+%! option = 'right';
+%! subplot (3,1,1);
+%!  plot (rand (1,4));
+%!  set (gca (), 'yaxislocation', 'right');
+%!  xlabel ('xlabel');
+%!  ylabel ('ylabel');
+%!  title ('Subplots should adjust to the legend placed outside');
+%!  legend ({'1'}, 'location', 'northeastoutside');
+%!  legend (option);
+%! subplot (3,1,2);
+%!  plot (rand (1,4));
+%!  set (gca (), 'yaxislocation', 'right');
+%!  xlabel ('xlabel');
+%!  ylabel ('ylabel');
+%!  legend ({'1234567890'}, 'location', 'eastoutside');
+%!  legend (option);
+%! subplot (3,1,3);
+%!  plot (rand (1,4));
+%!  set (gca (), 'yaxislocation', 'right');
+%!  xlabel ('xlabel');
+%!  ylabel ('ylabel');
+%!  legend ({'12345678901234567890'}, 'location', 'southeastoutside');
+%!  legend (option);
+
+%!demo % bug 36408
+%! clf;
+%! option = 'right';
+%! subplot (3,1,1);
+%!  plot (rand (1,4));
+%!  set (gca (), 'yaxislocation', 'right');
+%!  xlabel ('xlabel');
+%!  ylabel ('ylabel');
+%!  title ('Subplots should adjust to the legend placed outside');
+%!  legend ({'1'}, 'location', 'northwestoutside');
+%!  legend (option);
+%! subplot (3,1,2);
+%!  plot (rand (1,4));
+%!  set (gca (), 'yaxislocation', 'right');
+%!  xlabel ('xlabel');
+%!  ylabel ('ylabel');
+%!  legend ({'1234567890'}, 'location', 'westoutside');
+%!  legend (option);
+%! subplot (3,1,3);
+%!  plot (rand (1,4));
+%!  set (gca (), 'yaxislocation', 'right');
+%!  xlabel ('xlabel');
+%!  ylabel ('ylabel');
+%!  legend ({'12345678901234567890'}, 'location', 'southwestoutside');
+%!  legend (option);
+
+%!demo % bug 36408;
+%! clf;
+%! option = 'right';
+%! subplot (3,1,1);
+%!  plot (rand (1,4));
+%!  set (gca (), 'xaxislocation', 'top');
+%!  xlabel ('xlabel');
+%!  ylabel ('ylabel');
+%!  title ('Subplots should adjust to the legend placed outside');
+%!  legend ({'1'}, 'location', 'northwestoutside');
+%!  legend (option);
+%! subplot (3,1,2);
+%!  plot (rand (1,4));
+%!  set (gca (), 'xaxislocation', 'top');
+%!  xlabel ('xlabel');
+%!  ylabel ('ylabel');
+%!  legend ({'1234567890'}, 'location', 'westoutside');
+%!  legend (option);
+%! subplot (3,1,3);
+%!  plot (rand (1,4));
+%!  set (gca (), 'xaxislocation', 'top');
+%!  xlabel ('xlabel');
+%!  ylabel ('ylabel');
+%!  legend ({'12345678901234567890'}, 'location', 'southwestoutside');
+%!  legend (option);
+
+%!demo % bug 39697
+%! clf;
+%! plot (1:10);
+%! legend ('Legend Text');
+%! title ({'Multi-line', 'titles', 'are a', 'problem'});
+
+%!demo
+%! clf;
+%! colormap (cool (64));
+%! surf (peaks ());
+%! legend ('peaks()')
+%! title ('legend() works for surface objects too');
+
+%!test
+%! toolkit = graphics_toolkit ("gnuplot");
+%! h = figure ("visible", "off");
+%! unwind_protect
+%!   position = get (h, "position");
+%!   plot (rand (3));
+%!   legend ();
+%!   filename = sprintf ("%s.eps", tmpnam ());
+%!   print (filename);
+%!   unlink (filename);
+%!   assert (get (h, "position"), position);
+%! unwind_protect_cleanup
+%!   close (h);
+%!   graphics_toolkit (toolkit);
+%! end_unwind_protect
+