## Copyright (C) 2005-2018 John W. Eaton
##
## 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
## <https://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn  {} {} axes ()
## @deftypefnx {} {} axes (@var{property}, @var{value}, @dots{})
## @deftypefnx {} {} axes (@var{hax})
## @deftypefnx {} {@var{h} =} axes (@dots{})
## Create a Cartesian axes object and return a handle to it, or set the current
## axes to @var{hax}.
##
## Called without any arguments, or with @var{property}/@var{value} pairs,
## construct a new axes.
##
## Called with a single axes handle argument @var{hax}, the function makes
## @var{hax} the current axes (as returned by @code{gca}).  It also makes
## the figure which contains @var{hax} the current figure (as returned by
## @code{gcf}).  Finally, it restacks the parent object's @code{children}
## property so that the axes @var{hax} appears before all other axes handles
## in the list.  This causes @var{hax} to be displayed on top of any other axes
## objects (Z-order stacking).  In addition it restacks any legend or colorbar
## objects associated with @var{hax} so that they are also visible.
##
## Programming Note: The full list of properties is documented at
## @ref{Axes Properties,,Axes Properties}.
## @seealso{gca, set, get}
## @end deftypefn

## Author: jwe

function h = axes (varargin)

  if (nargin == 0 || nargin > 1)
    ## Parent handle
    idx = find (strcmpi (varargin(1:2:end), "parent"), 1, "first");
    if (! isempty (idx) && numel (varargin) >= 2*idx)
      hpar = varargin{2*idx};
      varargin([2*idx-1, 2*idx]) = [];
    else
      hpar = gcf ();
    endif

    ## If there is an annotation axes currently on top of the children stack,
    ## then it must be placed back on top.
    ## FIXME: It may be necessary to keep uiXXX objects above all axes objects
    ##        including even the transparent scribe axes layer.
    ch = allchild (hpar);
    h_annotation = ch(strcmp (get (ch, "tag"), "scribeoverlay"));

    ## Create an axes object.
    htmp = __go_axes__ (hpar, varargin{:});
    if (__is_handle_visible__ (htmp))
      set (ancestor (hpar, "figure"), "currentaxes", htmp);
    endif

    ## Restack annotation object if necessary
    if (! isempty (h_annotation))
      ## FIXME: This will put annotation layer first, above even uicontrol
      ## objects.  May need to keep annotation layer above all axes only.
      shh = get (0, "ShowHiddenHandles");
      unwind_protect
        set (0, "ShowHiddenHandles", "on");
        ch(ch == h_annotation) = htmp;
        ch = [h_annotation; ch];
        set (hpar, "children", ch);
      unwind_protect_cleanup
        set (0, "ShowHiddenHandles", shh);
      end_unwind_protect
    endif

  else
    ## ARG is axes handle.
    htmp = varargin{1};
    if (! isscalar (htmp) || ! isaxes (htmp))
      error ("axes: H must be a scalar axes handle");
    endif

    cf = ancestor (htmp, "figure");
    if (__is_handle_visible__ (htmp))
      set (0, "currentfigure", cf);
      set (cf, "currentaxes", htmp);
    endif

    restack_axes (htmp, get (htmp, "parent"));
  endif

  if (nargout > 0)
    h = htmp;
  endif

endfunction

function restack_axes (h, hpar)

  shh = get (0, "ShowHiddenHandles");
  unwind_protect
    set (0, "ShowHiddenHandles", "on");
    ch = get (hpar, "children");
    axidx = strcmp (get (ch, "type"), "axes");
    ## Strip out any annotation axes layer, unless h itself is annotation axes.
    if (! strcmp (get (h, "tag"), "scribeoverlay"))
      axidx(axidx) = ! strcmp (get (ch(axidx), "tag"), "scribeoverlay");
    endif
    hax = ch(axidx);  # List of axes

    ## Find and stack any legend belonging to this axes above this axes.
    try
      hleg = get (h, "__legend_handle__");
    catch
      hleg = false;
    end_try_catch

    ## Find and stack any colorbar belonging to this axes above this axes.
    try
      hcb = get (h, "__colorbar_handle__");
    catch
      hcb = false;
    end_try_catch

    ## Preserve order of colorbars and legends above this axes
    if (hleg || hcb)
      if (hleg && ! hcb)
        h = [hleg; h];
      elseif (hcb && ! hleg)
        h = [hcb; h];
      else
        hcb_idx = find (hcb == hax);
        hleg_idx = find (hleg == hax);
        if (hleg_idx < hcb_idx)
          h = [hleg; hcb; h];
        else
          h = [hcb; hleg; h];
        endif
      endif
    endif

    ## FIXME: ismember call is very slow (2/3rds of runtime for function)
    ch(axidx) = [h; hax(! ismember (hax, h))];
    set (hpar, "children", ch);

  unwind_protect_cleanup
    set (0, "ShowHiddenHandles", shh);
  end_unwind_protect

endfunction


## FIXME: These demos actually just show how axes objects behave.
##        They do not show how the axes() function itself works.
%!demo
%! clf;
%! x = -10:10;
%! plot (x,x, x,-x);
%! set (gca, "yscale", "log");
%! legend ({"x >= 1", "x <= 1"}, "location", "north");
%! title ({"log axes discard negative data", "ylim = [1, 10]"});

%!demo
%! clf;
%! x = -10:0.1:10;
%! y = sin (x)./(1 + abs (x)) + 0.1*x - 0.4;
%! plot (x, y);
%! set (gca, "xaxislocation", "origin");
%! set (gca, "yaxislocation", "origin");
%! box off;
%! title ({"no plot box", "xaxislocation = origin, yaxislocation = origin"});

%!demo
%! clf;
%! x = -10:0.1:10;
%! y = sin (x)./(1+abs (x)) + 0.1*x - 0.4;
%! plot (x, y);
%! set (gca, "xaxislocation", "origin");
%! set (gca, "yaxislocation", "left");
%! box off;
%! title ({"no plot box", "xaxislocation = origin, yaxislocation = left"});

%!demo
%! clf;
%! x = -10:0.1:10;
%! y = sin (x)./(1+abs (x)) + 0.1*x - 0.4;
%! plot (x, y);
%! title ("no plot box");
%! set (gca, "xaxislocation", "origin");
%! set (gca, "yaxislocation", "right");
%! box off;
%! title ({"no plot box", "xaxislocation = origin, yaxislocation = right"});

%!demo
%! clf;
%! x = -10:0.1:10;
%! y = sin (x)./(1+abs (x)) + 0.1*x - 0.4;
%! plot (x, y);
%! set (gca, "xaxislocation", "bottom");
%! set (gca, "yaxislocation", "origin");
%! box off;
%! title ({"no plot box", "xaxislocation = bottom, yaxislocation = origin"});

%!demo
%! clf;
%! x = -10:0.1:10;
%! y = sin (x)./(1+abs (x)) + 0.1*x - 0.4;
%! plot (x, y);
%! set (gca, "xaxislocation", "top");
%! set (gca, "yaxislocation", "origin");
%! box off;
%! title ({"no plot box", "xaxislocation = top, yaxislocation = origin"});

%!test
%! hf = figure ("visible", "off");
%! unwind_protect
%!   hax1 = axes ("tag", "axes1");
%!   plot (1:10, "b");
%!   hleg1 = legend ("leg1", "location", "east");
%!   hcb1 = colorbar ("location", "east");
%!   hanno = annotation ("arrow");
%!   hscribe = get (hanno, "parent");
%!
%!   hax2 = axes ("tag", "axes2");
%!   plot (10:-1:1, "r");
%!   hcb2 = colorbar ("location", "east");
%!   hleg2 = legend ("hax2");
%!
%!   ## Verify base configuration
%!   ch = allchild (hf);
%!   hax = ch(isaxes (ch));
%!   hax1pos = find (hax1 == hax);
%!   hax2pos = find (hax2 == hax);
%!   hleg1pos = find (hleg1 == hax);
%!   hleg2pos = find (hleg2 == hax);
%!   hcb1pos = find (hcb1 == hax);
%!   hcb2pos = find (hcb2 == hax);
%!   hscribepos = find (hscribe == hax);
%!
%!   assert (all (hscribepos < ...
%!                [hax1pos, hax2pos, hleg1pos, hleg2pos, hcb1pos, hcb2pos]));
%!   assert (hax2pos < hax1pos);
%!   assert (hleg2pos < hcb2pos && hcb2pos < hax2pos);
%!   assert (hcb1pos < hleg1pos && hleg1pos < hax1pos);
%!
%!   ## Restack axes1 on top
%!   axes (hax1);
%!   ch = allchild (hf);
%!   hax = ch(isaxes (ch));
%!   hax1pos = find (hax1 == hax);
%!   hax2pos = find (hax2 == hax);
%!   hleg1pos = find (hleg1 == hax);
%!   hleg2pos = find (hleg2 == hax);
%!   hcb1pos = find (hcb1 == hax);
%!   hcb2pos = find (hcb2 == hax);
%!   hscribepos = find (hscribe == hax);
%!
%!   assert (all (hscribepos < ...
%!                [hax1pos, hax2pos, hleg1pos, hleg2pos, hcb1pos, hcb2pos]));
%!   assert (hax1pos < hax2pos);
%!   assert (hcb1pos < hleg1pos && hleg1pos < hax1pos);
%!   assert (hleg2pos < hcb2pos && hcb2pos < hax2pos);
%!
%!   ## Restack axes2 on top
%!   axes (hax2);
%!   ch = allchild (hf);
%!   hax = ch(isaxes (ch));
%!   hax1pos = find (hax1 == hax);
%!   hax2pos = find (hax2 == hax);
%!   hleg1pos = find (hleg1 == hax);
%!   hleg2pos = find (hleg2 == hax);
%!   hcb1pos = find (hcb1 == hax);
%!   hcb2pos = find (hcb2 == hax);
%!   hscribepos = find (hscribe == hax);
%!
%!   assert (all (hscribepos < ...
%!                [hax1pos, hax2pos, hleg1pos, hleg2pos, hcb1pos, hcb2pos]));
%!   assert (hax2pos < hax1pos);
%!   assert (hleg2pos < hcb2pos && hcb2pos < hax2pos);
%!   assert (hcb1pos < hleg1pos && hleg1pos < hax1pos);
%!
%! unwind_protect_cleanup
%!   close (hf);
%! end_unwind_protect
