]> Creatis software - CreaPhase.git/blob - octave_packages/m/plot/legend.m
update packages
[CreaPhase.git] / octave_packages / m / plot / legend.m
1 ## Copyright (C) 2010-2012 David Bateman
2 ##
3 ## This file is part of Octave.
4 ##
5 ## Octave is free software; you can redistribute it and/or modify it
6 ## under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 3 of the License, or (at
8 ## your option) any later version.
9 ##
10 ## Octave is distributed in the hope that it will be useful, but
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 ## General Public License for more details.
14 ##
15 ## You should have received a copy of the GNU General Public License
16 ## along with Octave; see the file COPYING.  If not, see
17 ## <http://www.gnu.org/licenses/>.
18
19 ## -*- texinfo -*-
20 ## @deftypefn  {Function File} {} legend (@var{str1}, @var{str2}, @dots{})
21 ## @deftypefnx {Function File} {} legend (@var{matstr})
22 ## @deftypefnx {Function File} {} legend (@var{cell})
23 ## @deftypefnx {Function File} {} legend (@dots{}, "location", @var{pos})
24 ## @deftypefnx {Function File} {} legend (@dots{}, "orientation", @var{orient})
25 ## @deftypefnx {Function File} {} legend (@var{hax}, @dots{})
26 ## @deftypefnx {Function File} {} legend (@var{hobjs}, @dots{})
27 ## @deftypefnx {Function File} {} legend (@var{hax}, @var{hobjs}, @dots{})
28 ## @deftypefnx {Function File} {} legend ("@var{option}")
29 ##
30 ## Display a legend for the axes with handle @var{hax}, or the current axes,
31 ## using the specified strings as labels.  Legend entries may be specified
32 ## as individual character string arguments, a character array, or a cell
33 ## array of character strings.  If the handles, @var{hobjs}, are not specified
34 ## then the legend's strings will be associated with the axes' descendants.
35 ## Legend works on line graphs, bar graphs, etc.
36 ## A plot must exist before legend is called.
37 ##
38 ## The optional parameter @var{pos} specifies the location of the legend
39 ## as follows:
40 ##
41 ## @multitable @columnfractions 0.06 0.14 0.80
42 ##
43 ## @headitem @tab @var{pos} @tab
44 ##   location of the legend
45 ##
46 ## @item @tab north @tab
47 ##   center top
48 ##
49 ## @item @tab south @tab
50 ##   center bottom
51 ##
52 ## @item @tab east @tab
53 ##   right center
54 ##
55 ## @item @tab west @tab
56 ##   left center
57 ##
58 ## @item @tab northeast @tab
59 ##   right top (default)
60 ##
61 ## @item @tab northwest @tab
62 ##   left top
63 ##
64 ## @item @tab southeast @tab
65 ##   right bottom
66 ##
67 ## @item @tab southwest @tab
68 ##   left bottom
69 ##
70 ## @item
71 ##
72 ## @item @tab outside @tab
73 ##   can be appended to any location string
74 ## @end multitable
75 ##
76 ## The optional parameter @var{orient} determines if the key elements
77 ## are placed vertically or horizontally.  The allowed values are "vertical"
78 ## or "horizontal" with the default being "vertical".
79 ##
80 ## The following customizations are available using @var{option}:
81 ##
82 ## @table @asis
83 ## @item "show"
84 ##   Show legend on the plot
85 ##
86 ## @item "hide"
87 ##   Hide legend on the plot
88 ##
89 ## @itemx "toggle"
90 ##   Toggles between "hide" and "show"
91 ##
92 ## @item "boxon"
93 ##   Show a box around legend
94 ##
95 ## @item "boxoff"
96 ##   Hide the box around legend
97 ##
98 ## @item "left"
99 ##   Place text to the left of the keys
100 ##
101 ## @item "right"
102 ##   Place text to the right of the keys
103 ##
104 ## @itemx "off"
105 ##   Delete the legend object
106 ## @end table
107 ## @end deftypefn
108
109 function [hlegend2, hobjects2, hplot2, text_strings2] = legend (varargin)
110
111   if (nargin > 0
112       && (! ishandle (varargin{1})
113           || (strcmp (get (varargin{1}, "type"), "axes")
114               && ! strcmp (get (varargin{1}, "tag"), "legend"))))
115     [ca, varargin, nargs] = __plt_get_axis_arg__ ("legend", varargin{:});
116     fig = get (ca, "parent");
117   else
118     fig = get (0, "currentfigure");
119     if (isempty (fig))
120       fig = gcf ();
121     endif
122     ca = gca ();
123   endif
124
125   if (ishandle (ca) && isprop (ca, "__plotyy_axes__"))
126     plty = get (ca, "__plotyy_axes__");
127     if (isscalar (plty) && ishandle (plty))
128       ca = [ca, plty];
129     elseif (iscell (plty))
130       ca = [ca, plty{:}];
131     elseif (all (ishandle (plty)))
132       ca = [ca, plty(:).'];
133     else
134       error ("legend.m: This should not happen. File a bug report.")
135     endif
136     ## Remove duplicates while preserving order
137     [~, n] = unique (ca);
138     ca = ca (sort (n));
139   endif
140
141   if (nargin > 0 && all (ishandle (varargin{1})))
142     kids = flipud (varargin{1}(:));
143     varargin(1) = [];
144   else
145     kids = ca;
146     kids (strcmp (get (ca, "tag"), "legend")) = [];
147     if (isscalar (kids))
148       kids = get(kids, "children")(:);
149     else
150       kids = [get(kids, "children"){:}](:);
151     endif
152   endif
153   nargs = numel (varargin);
154   nkids = numel (kids);
155
156   orientation = "default";
157   position = "default";
158   show = "create";
159   textpos = "default";
160   box = "default";
161
162   if (nargs > 0)
163     pos = varargin{nargs};
164     if (isnumeric (pos) && isscalar (pos) && pos == fix (pos))
165       if (pos >= -1 && pos <= 4)
166         position = [{"northeastoutside", "best", "northeast",
167                      "northwest", "southwest", "southeast"}] {pos + 2};
168         nargs--;
169       else
170         error ("legend: invalid position specified");
171       endif
172     endif
173   endif
174
175   while (nargs > 1)
176     pos = varargin{nargs-1};
177     str = varargin{nargs};
178     if (strcmpi (pos, "location")  && ischar (str))
179       position = lower (str);
180       nargs -= 2;
181     elseif (strcmpi (pos, "orientation")  && ischar (str))
182       orientation = lower (str);
183       nargs -= 2;
184     else
185       break;
186     endif
187   endwhile
188
189   ## Validate the orientation
190   switch (orientation)
191     case {"vertical", "horizontal","default"}
192     otherwise
193       error ("legend: unrecognized legend orientation");
194   endswitch
195
196   ## Validate the position type is valid
197   outside = false;
198   inout = findstr (position, "outside");
199   if (! isempty (inout))
200     outside = true;
201     position = position(1:inout-1);
202   else
203     outside = false;
204   endif
205
206   switch (position)
207     case {"north", "south", "east", "west", "northeast", "northwest", ...
208           "southeast", "southwest", "default"}
209     case "best"
210       warning ("legend: 'Best' not yet implemented for location specifier\n");
211       position = "northeast";
212     otherwise
213       error ("legend: unrecognized legend position");
214   endswitch
215
216   hlegend = [];
217   fkids = get (fig, "children");
218   for i = 1 : numel(fkids)
219     if (ishandle (fkids (i)) && strcmp (get (fkids (i), "type"), "axes")
220         && (strcmp (get (fkids (i), "tag"), "legend")))
221       udata = get (fkids (i), "userdata");
222       if (! isempty (intersect (udata.handle, ca)))
223         hlegend = fkids (i);
224         break;
225       endif
226     endif
227   endfor
228
229   if (nargs == 1)
230     arg = varargin{1};
231     if (ischar (arg))
232       if (rows (arg) == 1)
233         str = tolower (deblank (arg));
234         switch (str)
235           case {"off"}
236             delete (hlegend);
237             return
238           case {"hide"}
239             show = "off";
240             nargs--;
241           case "show"
242             show = "on";
243             nargs--;
244           case "toggle"
245             if (isempty (hlegend) || strcmp (get (hlegend, "visible"), "off"))
246               show = "on";
247             else
248               show = "off";
249             endif
250             nargs--;
251           case "boxon"
252             box = "on";
253             nargs--;
254           case "boxoff"
255             box = "off";
256             nargs--;
257           case "left"
258             textpos = "left";
259             nargs--;
260           case "right"
261             textpos = "right";
262             nargs--;
263           otherwise
264         endswitch
265       else
266         varargin = cellstr (arg);
267         nargs = numel (varargin);
268       endif
269     elseif (iscellstr (arg))
270       varargin = arg;
271       nargs = numel (varargin);
272     else
273       error ("legend: expecting argument to be a character string");
274     endif
275   endif
276
277   if (strcmp (show, "off"))
278     if (! isempty (hlegend))
279       set (get (hlegend, "children"), "visible", "off");
280       hlegend = [];
281     endif
282     hobjects = [];
283     hplots  = [];
284     text_strings = {};
285   elseif (strcmp (show, "on"))
286     if (! isempty (hlegend))
287       set (get (hlegend, "children"), "visible", "on");
288     else
289       hobjects = [];
290       hplots  = [];
291       text_strings = {};
292     endif
293   elseif (strcmp (box, "on"))
294     if (! isempty (hlegend))
295       set (hlegend, "visible", "on", "box", "on");
296     endif
297   elseif (strcmp (box, "off"))
298     if (! isempty (hlegend))
299       set (hlegend, "box", "off", "visible", "off");
300     endif
301   elseif (nargs == 0 && !(strcmp (position, "default") &&
302                           strcmp (orientation, "default")))
303     if (! isempty (hlegend))
304       hax = getfield (get (hlegend, "userdata"), "handle");
305       [hplots, text_strings] = __getlegenddata__ (hlegend);
306
307       if  (strcmp (position, "default"))
308         h = legend (hax, hplots, text_strings, "orientation", orientation);
309       elseif (strcmp (orientation, "default"))
310         if (outside)
311           h = legend (hax, hplots, text_strings, "location",
312                       strcat (position, "outside"));
313         else
314           h = legend (hax, hplots, text_strings, "location", position);
315         endif
316       else
317         if (outside)
318           h = legend (hax, hplots, text_strings, "location",
319                       strcat (position, "outside"), "orientation", orientation);
320         else
321           h = legend (hax, hplots, text_strings, "location", position,
322                       "orientation", orientation);
323         endif
324       endif
325     endif
326   else
327     hobjects = [];
328     hplots  = [];
329     text_strings = {};
330
331     if (nargs > 0)
332       have_data = false;
333       for k = 1:nkids
334         typ = get (kids(k), "type");
335         if (strcmp (typ, "line") || strcmp (typ, "surface")
336             || strcmp (typ, "patch") || strcmp (typ, "hggroup"))
337           have_data = true;
338           break;
339         endif
340       endfor
341
342       if (! have_data)
343         warning ("legend: plot data is empty; setting key labels has no effect");
344       endif
345     endif
346
347     if (strcmp (textpos, "default"))
348       warned = false;
349       k = nkids;
350       for i = 1 : nargs
351         arg = varargin{i};
352         if (ischar (arg))
353           typ = get (kids(k), "type");
354           while (k > 0
355                  && ! (strcmp (typ, "line") || strcmp (typ, "surface")
356                        || strcmp (typ, "patch") || strcmp (typ, "hggroup")))
357             typ = get (kids(--k), "type");
358           endwhile
359           if (k > 0)
360             if (strcmp (get (kids(k), "type"), "hggroup"))
361               hgkids = get (kids(k), "children");
362               for j = 1 : length (hgkids)
363                 hgobj = get (hgkids (j));
364                 if (isfield (hgobj, "displayname"))
365                   set (hgkids(j), "displayname", arg);
366                   hplots = [hplots, hgkids(j)];
367                   text_strings = {text_strings{:}, arg};
368                   break;
369                 endif
370               endfor
371             else
372               set (kids(k), "displayname", arg);
373               hplots = [hplots, kids(k)];
374               text_strings = {text_strings{:}, arg};
375             endif
376
377             if (--k == 0)
378               break;
379             endif
380           elseif (! warned)
381             break;
382           endif
383         else
384           error ("legend: expecting argument to be a character string");
385         endif
386       endfor
387       if (i < nargs && ! warned)
388         warning ("legend: ignoring extra labels");
389       endif
390     else
391       k = nkids;
392       while (k > 0)
393         typ = get (kids(k), "type");
394         while (k > 1
395                && ! (strcmp (typ, "line") || strcmp (typ, "surface")
396                      || strcmp (typ, "patch") || strcmp (typ, "hggroup")))
397           typ = get (kids(--k), "type");
398         endwhile
399         if (! (strcmp (typ, "line") || strcmp (typ, "surface")
400                || strcmp (typ, "patch") || strcmp (typ, "hggroup")))
401           break
402         endif
403         if (k > 0)
404           if (strcmp (get (kids(k), "type"), "hggroup"))
405             hgkids = get (kids(k), "children");
406             for j = 1 : length (hgkids)
407               hgobj = get (hgkids (j));
408               if (isfield (hgobj, "displayname")
409                   && ! isempty (hgobj.displayname))
410                 hplots = [hplots, hgkids(j)];
411                 text_strings = {text_strings{:}, hgobj.displayname};
412                 break;
413               endif
414             endfor
415           else
416             if (! isempty (get (kids (k), "displayname")))
417               hplots = [hplots, kids(k)];
418               text_strings = {text_strings{:}, get(kids (k), "displayname")};
419             endif
420           endif
421           if (--k == 0)
422             break;
423           endif
424         endif
425       endwhile
426     endif
427
428     if (isempty (hplots))
429       if (! isempty (hlegend))
430         fkids = get (fig, "children");
431         delete (fkids (fkids == hlegend));
432         hlegend = [];
433         hobjects = [];
434         hplots  = [];
435         text_strings = {};
436       endif
437     else
438       ## Delete the old legend if it exists
439       if (! isempty (hlegend))
440         if (strcmp (textpos, "default"))
441           textpos = get (hlegend, "textposition");
442         endif
443         if (strcmp (position, "default"))
444           position = get (hlegend, "location");
445           inout = findstr (position, "outside");
446           if (! isempty (inout))
447             outside = true;
448             position = position(1:inout-1);
449           else
450             outside = false;
451           endif
452         endif
453         if (strcmp (orientation, "default"))
454           orientation = get (hlegend, "orientation");
455         endif
456         box = get (hlegend, "box");
457         fkids = get (fig, "children");
458
459         delete (hlegend);
460         hlegend = [];
461       else
462         if (strcmp (textpos, "default"))
463           textpos = "left";
464         endif
465         if (strcmp (position, "default"))
466           position = "northeast";
467         endif
468         if (strcmp (orientation, "default"))
469           orientation = "vertical";
470         endif
471         box = "off";
472       endif
473
474       ## Get axis size and fontsize in points.
475       ## Rely on listener to handle coversion.
476       units = get (ca(1), "units");
477       fontunits = get (ca(1), "fontunits");
478       unwind_protect
479         set (ca(1), "units", "points");
480         set (ca(1), "fontunits", "points");
481         ca_pos = get (ca(1), "position");
482         ca_outpos = get (ca(1), "outerposition");
483         ca_fontsize = get (ca(1), "fontsize");
484       unwind_protect_cleanup
485         set (ca(1), "units", units);
486         set (ca(1), "fontunits", fontunits);
487       end_unwind_protect
488
489       ## Padding between legend entries horizontally and vertically
490       xpad = 2;
491       ypad = 2;
492
493       ## Length of line segments in the legend in points
494       linelength = 15;
495
496       ## Create the axis first
497       ## FIXME hlegend should inherit properties from "ca"
498       curaxes = get (fig, "currentaxes");
499       unwind_protect
500         ud = ancestor(hplots, "axes");
501         if (!isscalar(ud))
502           ud = unique ([ud{:}]);
503         endif
504         if (isempty (hlegend))
505           addprops = true;
506           hlegend = axes ("tag", "legend", "userdata", struct ("handle", ud),
507                           "box", box,
508                           "xtick", [], "ytick", [], "xticklabel", "",
509                           "yticklabel", "", "zticklabel", "",
510                           "xlim", [0, 1], "ylim", [0, 1], "visible", "off",
511                           "activepositionproperty", "position");
512         else
513           addprops = false;
514           axes (hlegend);
515           delete (get (hlegend, "children"));
516         endif
517
518         ## Add text label to the axis first, checking their extents
519         nentries = numel (hplots);
520         texthandle = [];
521         maxwidth = 0;
522         maxheight = 0;
523         for k = 1 : nentries
524           if (strcmp (textpos, "right"))
525             texthandle = [texthandle, text(0, 0, text_strings {k},
526                                            "horizontalalignment", "left",
527                                            "userdata", hplots(k))];
528           else
529             texthandle = [texthandle, text(0, 0, text_strings {k},
530                                            "horizontalalignment", "right",
531                                            "userdata", hplots(k))];
532           endif
533           units = get (texthandle (end), "units");
534           unwind_protect
535             set (texthandle (end), "units", "points");
536             extents = get (texthandle (end), "extent");
537             maxwidth = max (maxwidth, extents (3));
538             maxheight = max (maxheight, extents (4));
539           unwind_protect_cleanup
540             set (texthandle (end), "units", units);
541           end_unwind_protect
542         endfor
543
544         num1 = nentries;
545         if (strcmp (orientation, "vertical"))
546           height = nentries * (ypad + maxheight);
547           if (outside)
548             if (height > ca_pos (4))
549               ## Avoid shrinking the height of the axis to zero if outside
550               num1 = ca_pos(4) / (maxheight + ypad) / 2;
551             endif
552           else
553             if (height > 0.9 * ca_pos (4))
554               num1 = 0.9 * ca_pos(4) / (maxheight + ypad);
555             endif
556           endif
557         else
558           width = nentries * (ypad + maxwidth);
559           if (outside)
560             if (width > ca_pos (3))
561               ## Avoid shrinking the width of the axis to zero if outside
562               num1 = ca_pos(3) / (maxwidth + ypad) / 2;
563             endif
564           else
565             if (width > 0.9 * ca_pos (3))
566               num1 = 0.9 * ca_pos(3) / (maxwidth + ypad);
567             endif
568           endif
569         endif
570         num2 = ceil (nentries / num1);
571
572         xstep = 3 * xpad + (maxwidth + linelength);
573         if (strcmp (textpos, "right"))
574           xoffset = xpad;
575           txoffset = 2 * xpad + linelength;
576         else
577           xoffset = 2 * xpad + maxwidth;
578           txoffset = xpad + maxwidth;
579         endif
580         ystep = (ypad + maxheight);
581         yoffset = ystep / 2;
582
583         ## Place the legend in the desired position
584         if (strcmp (orientation, "vertical"))
585           lpos = [0, 0, num2 * xstep, num1 * ystep];
586         else
587           lpos = [0, 0, num1 * xstep, num2 * ystep];
588         endif
589         switch (position)
590           case "north"
591             if (outside)
592               lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
593                       ca_outpos(2) + ca_outpos(4) - lpos(4) - ypad, lpos(3), ...
594                       lpos(4)];
595
596               new_pos = [ca_pos(1), ca_pos(2), ca_pos(3), ca_pos(4) - lpos(4)];
597             else
598               lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
599                       ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), lpos(4)];
600             endif
601           case "south"
602             if (outside)
603               lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
604                       ca_outpos(2) + ypad, lpos(3), lpos(4)];
605               new_pos = [ca_pos(1), ca_pos(2) + lpos(4), ca_pos(3), ...
606                          ca_pos(4) - lpos(4)];
607             else
608               lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
609                       ca_pos(2) + ypad, lpos(3), lpos(4)];
610             endif
611           case "east"
612             if (outside)
613               lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3) - ypad, ...
614                       ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
615               new_pos = [ca_pos(1), ca_pos(2), ca_pos(3) - lpos(3), ca_pos(4)];
616             else
617               lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
618                       ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
619             endif
620           case "west"
621             if (outside)
622               lpos = [ca_outpos(1) + ypad, ...
623                       ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, ...
624                       lpos(3), lpos(4)];
625               new_pos = [ca_pos(1) + lpos(3), ca_pos(2), ...
626                          ca_pos(3) - lpos(3), ca_pos(4)];
627             else
628               lpos = [ca_pos(1) +  ypad, ...
629                       ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
630             endif
631           case "northeast"
632             if (outside)
633               lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3) - ypad, ...
634                       ca_pos(2) + ca_pos(4) - lpos(4), lpos(3), lpos(4)];
635               new_pos = [ca_pos(1), ca_pos(2), ca_pos(3) - lpos(3), ca_pos(4)];
636             else
637               lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
638                       ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), lpos(4)];
639             endif
640           case "northwest"
641             if (outside)
642               lpos = [ca_outpos(1) + ypad , ca_pos(2) + ca_pos(4) - lpos(4), ...
643                       lpos(3), lpos(4)];
644               new_pos = [ca_pos(1) + lpos(3), ca_pos(2), ...
645                          ca_pos(3) - lpos(3), ca_pos(4)];
646             else
647               lpos = [ca_pos(1) + ypad, ...
648                       ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), lpos(4)];
649             endif
650           case "southeast"
651             if (outside)
652               lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3) - ypad, ...
653                       ca_pos(2), lpos(3), lpos(4)];
654               new_pos = [ca_pos(1), ca_pos(2), ...
655                          ca_pos(3) - lpos(3), ca_pos(4)];
656             else
657               lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
658                       ca_pos(2) + ypad, lpos(3), lpos(4)];
659             endif
660           case "southwest"
661             if (outside)
662               lpos = [ca_outpos(1) + ypad, ca_pos(2), lpos(3), lpos(4)];
663               new_pos = [ca_pos(1) + lpos(3), ca_pos(2), ...
664                          ca_pos(3) - lpos(3), ca_pos(4)];
665             else
666               lpos = [ca_pos(1) + ypad, ca_pos(2) + ypad, lpos(3), lpos(4)];
667             endif
668         endswitch
669
670         units = get (hlegend, "units");
671         unwind_protect
672           set (hlegend, "units", "points");
673           set (hlegend, "position", lpos);
674         unwind_protect_cleanup
675           set (hlegend, "units", units);
676         end_unwind_protect
677
678         ## Now write the line segments and place the text objects correctly
679         xk = 0;
680         yk = 0;
681         for k = 1 : numel (hplots)
682           hobjects = [hobjects, texthandle (k)];
683           switch (get (hplots(k), "type"))
684           case "line"
685             color = get (hplots(k), "color");
686             style = get (hplots(k), "linestyle");
687             if (! strcmp (style, "none"))
688               l1 = line ("xdata", ([xoffset, xoffset + linelength] + xk * xstep) / lpos(3),
689                          "ydata", [1, 1] .* (lpos(4) - yoffset - yk * ystep) / lpos(4),
690                          "color", color, "linestyle", style, "marker", "none",
691                          "userdata", hplots (k));
692               hobjects = [hobjects, l1];
693             endif
694             marker = get (hplots(k), "marker");
695             if (! strcmp (marker, "none"))
696               l1 = line ("xdata", (xoffset + 0.5 * linelength  + xk * xstep) / lpos(3),
697                          "ydata", (lpos(4) - yoffset - yk * ystep) / lpos(4),
698                          "color", color, "linestyle", "none", "marker", marker,
699                          "markeredgecolor", get (hplots (k), "markeredgecolor"),
700                          "markerfacecolor", get (hplots (k), "markerfacecolor"),
701                          "markersize", get (hplots (k), "markersize"),
702                          "userdata", hplots (k));
703               hobjects = [hobjects, l1];
704             endif
705
706             addlistener(hplots(k), "color", {@updateline, hlegend, linelength});
707             addlistener(hplots(k), "linestyle", {@updateline, hlegend, linelength});
708             addlistener(hplots(k), "marker", {@updateline, hlegend, linelength});
709             addlistener(hplots(k), "markeredgecolor", {@updateline, hlegend, linelength});
710             addlistener(hplots(k), "markerfacecolor", {@updateline, hlegend, linelength});
711             addlistener(hplots(k), "markersize", {@updateline, hlegend, linelength});
712             addlistener(hplots(k), "displayname", {@updateline, hlegend, linelength});
713           case "patch"
714           case "surface"
715           endswitch
716           set (texthandle (k), "position", [(txoffset + xk * xstep) / lpos(3), ...
717                                             (lpos(4) - yoffset - yk * ystep) / lpos(4)]);
718           if (strcmp (orientation, "vertical"))
719             yk++;
720             if (yk > num1)
721               yk = 0;
722               xk++;
723             endif
724           else
725             xk++;
726             if (xk > num1)
727               xk = 0;
728               yk++;
729             endif
730           endif
731         endfor
732
733         ## Add an invisible text object to original axis
734         ## that when it is destroyed will remove the legend
735         t1 = text (0, 0, "", "parent", ca(1), "tag", "legend",
736                    "handlevisibility", "off", "visible", "off",
737                    "xliminclude", "off", "yliminclude", "off");
738         set (t1, "deletefcn", {@deletelegend1, hlegend});
739
740         ## Resize the axis the legend is attached to if the
741         ## legend is "outside" the plot and create listener to
742         ## resize axis to original size if the legend is deleted,
743         ## hidden or shown
744         if (outside)
745           for i = 1 : numel (ca)
746             units = get (ca(i), "units");
747             unwind_protect
748               set (ca(i), "units", "points");
749               set (ca (i), "position", new_pos);
750             unwind_protect_cleanup
751               set (ca(i), "units", units);
752             end_unwind_protect
753           endfor
754
755           set (hlegend, "deletefcn", {@deletelegend2, ca, ...
756                                       ca_pos, ca_outpos, t1, hplots});
757           addlistener (hlegend, "visible", {@hideshowlegend, ca, ...
758                                             ca_pos, new_pos});
759         else
760           set (hlegend, "deletefcn", {@deletelegend2, ca, [], [], t1, hplots});
761         endif
762
763         if (addprops)
764           addproperty ("edgecolor", hlegend, "color", [0, 0, 0]);
765           addproperty ("textcolor", hlegend, "color", [0, 0, 0]);
766           addproperty ("location", hlegend, "radio", "north|south|east|west|{northeast}|southeast|northwest|southwest|northoutside|southoutside|eastoutside|westoutside|northeastoutside|southeastoutside|northwestoutside|southwestoutside");
767           addproperty ("orientation", hlegend, "radio",
768                        "{vertical}|horizontal");
769           addproperty ("string", hlegend, "any", text_strings);
770           addproperty ("textposition", hlegend, "radio", "{left}|right");
771         else
772           set (hlegend, "string", text_strings);
773         endif
774
775         if (outside)
776           set (hlegend, "location", strcat (position, "outside"),
777                "orientation", orientation, "textposition", textpos);
778         else
779           set (hlegend, "location", position, "orientation", orientation,
780                "textposition", textpos);
781         endif
782         if (addprops)
783           addlistener (hlegend, "edgecolor", @updatelegendtext);
784           addlistener (hlegend, "textcolor", @updatelegendtext);
785           addlistener (hlegend, "interpreter", @updatelegendtext);
786           addlistener (hlegend, "location", @updatelegend);
787           addlistener (hlegend, "orientation", @updatelegend);
788           addlistener (hlegend, "string", @updatelegend);
789           addlistener (hlegend, "textposition", @updatelegend);
790         endif
791       unwind_protect_cleanup
792         set (fig, "currentaxes", curaxes);
793       end_unwind_protect
794     endif
795   endif
796
797   if (nargout > 0)
798     hlegend2 = hlegend;
799     hobjects2 = hobjects;
800     hplot2 = hplots;
801     text_strings2 = text_strings;
802   endif
803
804 endfunction
805
806 function updatelegend (h, d)
807   persistent recursive = false;
808   if (! recursive)
809     recursive = true;
810     unwind_protect
811       hax = getfield (get (h, "userdata"), "handle");
812       [hplots, text_strings] = __getlegenddata__ (h);
813       h = legend (hax, hplots, get (h, "string"));
814     unwind_protect_cleanup
815       recursive = false;
816     end_unwind_protect
817   endif
818 endfunction
819
820 function updatelegendtext (h, d)
821   hax = get (h, "userdata").handle;
822   kids = get (h, "children");
823   text_kids = findobj (kids, "-property", "interpreter", "type", "text");
824   interpreter = get (h, "interpreter");
825   textcolor = get (h, "textcolor");
826   set (kids, "interpreter", interpreter, "color", textcolor);
827   hobj = cell2mat (get (kids, "userdata"));
828   set (hobj, "interpreter", interpreter);
829 endfunction
830
831 function hideshowlegend (h, d, ca, pos1, pos2)
832   isvisible = strcmp (get (h, "visible"), "off");
833   if (! isvisible)
834     kids = get (h, "children");
835     for i = 1 : numel (kids)
836       if (! strcmp (get (kids(i), "visible"), "off"))
837         isvisible = true;
838         break;
839       endif
840     endfor
841   endif
842
843   for i = 1 : numel (ca)
844     if (ishandle (ca(i)) && strcmp (get (ca(i), "type"), "axes")
845         && (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off"))
846         && strcmp (get (ca(i), "beingdeleted"), "off"))
847       units = get (ca(i), "units");
848       unwind_protect
849         set (ca(i), "units", "points");
850         if (isvisible)
851           set (ca(i), "position", pos2);
852         else
853           set (ca(i), "position", pos1);
854         endif
855       unwind_protect_cleanup
856         set (ca(i), "units", units);
857       end_unwind_protect
858     endif
859   endfor
860 endfunction
861
862 function deletelegend1 (h, d, ca)
863   if (ishandle (ca) && strcmp (get (ca, "type"), "axes")
864       && (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off"))
865       && strcmp (get (ca, "beingdeleted"), "off"))
866     delete (ca);
867   endif
868 endfunction
869
870 function deletelegend2 (h, d, ca, pos, outpos, t1, hplots)
871   for i = 1 : numel (ca)
872     if (ishandle (ca(i)) && strcmp (get (ca(i), "type"), "axes")
873         && (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off"))
874         && strcmp (get (ca(i), "beingdeleted"), "off"))
875       if (!isempty (pos) && !isempty(outpos))
876         units = get (ca(i), "units");
877         unwind_protect
878           set (ca(i), "units", "points");
879           set (ca(i), "position", pos, "deletefcn", "");
880         unwind_protect_cleanup
881           set (ca(i), "units", units);
882         end_unwind_protect
883       endif
884     endif
885   endfor
886   set (t1, "deletefcn", "");
887   delete (t1);
888   for i = 1 : numel (hplots)
889     if (strcmp (get (hplots (i), "type"), "line"))
890       dellistener (hplots (i), "color");
891       dellistener (hplots (i), "linestyle");
892       dellistener (hplots (i), "marker");
893       dellistener (hplots (i), "markeredgecolor");
894       dellistener (hplots (i), "markerfacecolor");
895       dellistener (hplots (i), "markersize");
896       dellistener (hplots (i), "displayname");
897     endif
898   endfor
899 endfunction
900
901 function updateline (h, d, hlegend, linelength)
902   lm = [];
903   ll = [];
904   kids = get (hlegend, "children");
905   for i = 1 : numel (kids)
906     if (get (kids (i), "userdata") == h
907         && strcmp (get (kids(i), "type"), "line"))
908       if (strcmp (get (kids (i), "marker"), "none"))
909         ll = kids (i);
910       else
911         lm = kids (i);
912       endif
913     endif
914   endfor
915
916   linestyle = get (h, "linestyle");
917   marker = get (h, "marker");
918   displayname = get (h, "displayname");
919
920   if ((isempty (displayname)
921        || (strcmp (marker, "none") && strcmp (linestyle, "none")))
922        && (! isempty (lm) || isempty (ll)))
923     ## An element was removed from the legend. Need to recall the
924     ## legend function to recreate a new legend
925     [hplots, text_strings] = __getlegenddata__ (hlegend);
926     for i = 1 : numel (hplots)
927       if (hplots (i) == h)
928         hplots(i) = [];
929         text_strings(i) = [];
930         break;
931       endif
932     endfor
933     legend (hplots, text_strings);
934   elseif ((!isempty (displayname)
935            && (! strcmp (marker, "none") || ! strcmp (linestyle, "none")))
936           && isempty (lm) && isempty (ll))
937     ## An element was added to the legend. Need to recall the
938     ## legend function to recreate a new legend
939     [hplots, text_strings] = __getlegenddata__ (hlegend);
940     hplots = [hplots, h];
941     text_strings = {text_strings{:}, displayname};
942     legend (hplots, text_strings);
943   else
944     if (! isempty (ll))
945       ypos1 = get (ll,"ydata");
946       xpos1 = get (ll,"xdata");
947       ypos2 = ypos1(1);
948       xpos2 = sum(xpos1) / 2;
949       delete (ll);
950       if (! isempty (lm))
951         delete (lm);
952       endif
953     else
954       ypos2 = get (lm,"ydata");
955       xpos2 = get (lm,"xdata");
956       ypos1 = [ypos2, ypos2];
957       xpos1 = xpos2 + [-0.5, 0.5] * linelength;
958       delete (lm);
959     endif
960     if (! strcmp (linestyle, "none"))
961       line ("xdata", xpos1, "ydata", ypos1, "color", get (h, "color"),
962             "linestyle", get (h, "linestyle"), "marker", "none",
963             "userdata", h, "parent", hlegend);
964     endif
965     if (! strcmp (marker, "none"))
966       line ("xdata", xpos2, "ydata", ypos2, "color", get (h, "color"),
967             "marker", marker, "markeredgecolor", get (h, "markeredgecolor"),
968             "markerfacecolor", get (h, "markerfacecolor"),
969             "markersize", get (h, "markersize"), "linestyle", "none",
970             "userdata", h, "parent", hlegend);
971     endif
972   endif
973 endfunction
974
975 %!demo
976 %! clf
977 %! x = 0:1;
978 %! plot (x, x, ";I am Blue;", x, 2*x, ";I am Green;", x, 3*x, ";I am Red;")
979
980 %!demo
981 %! clf
982 %! x = 0:1;
983 %! plot (x, x, ";I am Blue;", x, 2*x, x, 3*x, ";I am Red;")
984 %! title ("Blue and Green keys, with Green mising")
985
986 %!demo
987 %! clf
988 %! plot(1:10, 1:10, 1:10, fliplr(1:10));
989 %! title("incline is blue and decline is green");
990 %! legend({"I am blue", "I am green"}, "location", "east");
991 %! legend({"I am blue", "I am green"}, "location", "east");
992 %! legend hide
993 %! legend show
994
995 %!demo
996 %! clf
997 %! plot(1:10, 1:10, 1:10, fliplr(1:10));
998 %! title("Legend is hidden")
999 %! legend({"I am blue", "I am green"}, "location", "east");
1000 %! legend hide
1001
1002 %!demo
1003 %! clf
1004 %! plot(1:10, 1:10, 1:10, fliplr(1:10));
1005 %! title("Legend with box on")
1006 %! legend({"I am blue", "I am green"}, "location", "east");
1007 %! legend boxon
1008
1009 %!demo
1010 %! clf
1011 %! plot(1:10, 1:10, 1:10, fliplr(1:10));
1012 %! title("Legend with text to the right")
1013 %! legend({"I am blue", "I am green"}, "location", "east");
1014 %! legend right
1015
1016 %!demo
1017 %! clf
1018 %! plot(1:10, 1:10);
1019 %! title("a very long label can sometimes cause problems");
1020 %! legend({"hello world"}, "location", "northeastoutside");
1021
1022 %!demo
1023 %! clf
1024 %! plot(1:10, 1:10);
1025 %! title("a very long label can sometimes cause problems");
1026 %! legend("hello world", "location", "northeastoutside");
1027
1028 %!demo
1029 %! clf
1030 %! labels = {};
1031 %! colororder = get (gca, "colororder");
1032 %! for i = 1:5
1033 %!   h = plot(1:100, i + rand(100,1)); hold on;
1034 %!   set (h, "color", colororder(i,:))
1035 %!   labels = {labels{:}, cstrcat("Signal ", num2str(i))};
1036 %! endfor
1037 %! hold off;
1038 %! title("Signals with random offset and uniform noise")
1039 %! xlabel("Sample Nr [k]"); ylabel("Amplitude [V]");
1040 %! legend(labels, "location", "southoutside");
1041 %! legend("boxon");
1042
1043 %!demo
1044 %! clf
1045 %! labels = {};
1046 %! colororder = get (gca, "colororder");
1047 %! for i = 1:5
1048 %!   h = plot(1:100, i + rand(100,1)); hold on;
1049 %!   set (h, "color", colororder(i,:))
1050 %!   labels = {labels{:}, cstrcat("Signal ", num2str(i))};
1051 %! endfor
1052 %! hold off;
1053 %! title("Signals with random offset and uniform noise")
1054 %! xlabel("Sample Nr [k]"); ylabel("Amplitude [V]");
1055 %! legend(labels{:}, "location", "southoutside")
1056 %! legend("boxon")
1057
1058 %!demo
1059 %! clf
1060 %! x = linspace (0, 10);
1061 %! plot (x, x);
1062 %! hold ("on");
1063 %! stem (x, x.^2, 'g')
1064 %! legend ("linear");
1065 %! hold ("off");
1066
1067 %!demo
1068 %! clf
1069 %! x = linspace (0, 10);
1070 %! plot (x, x, x, x.^2);
1071 %! legend ("linear");
1072
1073 %!demo
1074 %! clf
1075 %! x = linspace (0, 10);
1076 %! plot (x, x, x, x.^2);
1077 %! legend ("linear", "quadratic");
1078
1079 %!demo
1080 %! clf
1081 %! rand_2x3_data1 = [0.341447, 0.171220, 0.284370; 0.039773, 0.731725, 0.779382];
1082 %! bar (rand_2x3_data1);
1083 %! ylim ([0 1.0]);
1084 %! legend ({"1st Bar", "2nd Bar", "3rd Bar"});
1085
1086 %!demo
1087 %! clf
1088 %! rand_2x3_data2 = [0.44804, 0.84368, 0.23012; 0.72311, 0.58335, 0.90531];
1089 %! bar (rand_2x3_data2);
1090 %! ylim ([0 1.2]);
1091 %! legend ("1st Bar", "2nd Bar", "3rd Bar");
1092 %! legend right
1093
1094 %!demo
1095 %! clf
1096 %! x = 0:0.1:7;
1097 %! h = plot (x, sin(x), x, cos(x), x, sin(x.^2/10), x, cos(x.^2/10));
1098 %! title ("Only the sin() objects have keylabels");
1099 %! legend (h([1, 3]), {"sin(x)", "sin(x^2/10)"}, "location", "southwest");
1100
1101 %!demo
1102 %! clf
1103 %! x = 0:0.1:10;
1104 %! plot (x, sin(x), ";sin(x);")
1105 %! hold all
1106 %! plot (x, cos(x), ";cos(x);")
1107 %! hold off
1108
1109 %!demo
1110 %! clf
1111 %! x = 0:0.1:10;
1112 %! plot (x, sin(x), ";sin(x);")
1113 %! hold all
1114 %! plot (x, cos(x), ";cos(x);")
1115 %! hold off
1116 %! legend ({"sin(x)", "cos(x)"}, "location", "northeastoutside")
1117
1118 %!demo
1119 %! clf
1120 %! x = 0:10;
1121 %! plot (x, rand (11));
1122 %! xlabel ("Indices")
1123 %! ylabel ("Random Values")
1124 %! title ("Legend ""off"" should delete the legend")
1125 %! legend (cellstr (num2str ((1:10)')), "location", "northeastoutside")
1126 %! legend off
1127 %! axis ([0, 10, 0 1])
1128
1129 %!demo
1130 %! clf
1131 %! x = 1:5;
1132 %! subplot (2, 2, 1)
1133 %! plot (x, rand (numel (x)));
1134 %! legend (cellstr (num2str (x')), "location", "northwestoutside")
1135 %! legend boxon
1136 %! subplot (2, 2, 2)
1137 %! plot (x, rand (numel (x)));
1138 %! legend (cellstr (num2str (x')), "location", "northeastoutside")
1139 %! legend boxon
1140 %! subplot (2, 2, 3);
1141 %! plot (x, rand (numel (x)));
1142 %! legend (cellstr (num2str (x')), "location", "southwestoutside")
1143 %! legend boxon
1144 %! subplot (2, 2, 4)
1145 %! plot (x, rand (numel (x)));
1146 %! legend (cellstr (num2str (x')), "location", "southeastoutside")
1147 %! legend boxon
1148
1149 %!demo
1150 %! clf
1151 %! plot (rand (2))
1152 %! title ("Warn of extra labels")
1153 %! legend ("Hello", "World", "interpreter", "foobar")
1154
1155 %!demo
1156 %! clf
1157 %! plot (rand (2))
1158 %! title ("Turn off TeX interpreter")
1159 %! h = legend ("Hello_World", "foo^bar");
1160 %! set (h, "interpreter", "none")
1161