# HG changeset patch
# User Guillaume Flandin
# Date 1521126760 0
#      Thu Mar 15 15:12:40 2018 +0000
# Node ID 6563aa3c4b44bf93d6eb9ff68848934e936a1d2f
# Parent  df131a44310bc0d6f3360696bea501c526d469a0
New function openfig.m (bug #44670)

* openfig.m: new wrapper around hgload.m.
* open.m: add .fig and .ofig file types.
* hgload.m: add option to override top-level properties. Add support
  for Matlab-named handle structures. Add tests.

diff -r df131a44310b -r 6563aa3c4b44 scripts/miscellaneous/open.m
--- a/scripts/miscellaneous/open.m	Thu Mar 15 15:04:41 2018 +0100
+++ b/scripts/miscellaneous/open.m	Thu Mar 15 15:12:40 2018 +0000
@@ -1,4 +1,4 @@
-## Copyright (C) 2014-2017 John W. Eaton
+## Copyright (C) 2014-2018 John W. Eaton
 ##
 ## This file is part of Octave.
 ##
@@ -31,6 +31,9 @@
 ## @item .mat
 ## Load the file in the base workspace.
 ##
+## @item .fig, .ofig
+## Load the figure
+##
 ## @item .exe
 ## Execute the program (on Windows systems only).
 ## @end table
@@ -58,7 +61,9 @@
     else
       evalin ("base", sprintf ("load ('%s');", file));
     endif
-  elseif (any (strcmpi (ext, {".fig", ".mdl", ".slx", ".prj"})))
+  elseif (any (strcmpi (ext, {".fig", ".ofig"})))
+    output = openfig (file);
+  elseif (any (strcmpi (ext, {".mdl", ".slx", ".prj"})))
     error ("open: opening file type '%s' is not supported", ext);
   elseif (strcmpi (ext, ".exe"))
     if (ispc ())
diff -r df131a44310b -r 6563aa3c4b44 scripts/plot/util/hgload.m
--- a/scripts/plot/util/hgload.m	Thu Mar 15 15:04:41 2018 +0100
+++ b/scripts/plot/util/hgload.m	Thu Mar 15 15:12:40 2018 +0000
@@ -1,4 +1,4 @@
-## Copyright (C) 2014-2017 Massimiliano Fasi
+## Copyright (C) 2014-2018 Massimiliano Fasi
 ##
 ## This file is part of Octave.
 ##
@@ -18,22 +18,35 @@
 
 ## -*- texinfo -*-
 ## @deftypefn {} {@var{h} =} hgload (@var{filename})
+## @deftypefnx {} {[@var{h}, @var{old_prop}] =} hgload (@var{filename}, @var{prop_struct})
 ## Load the graphics object in @var{filename} into the graphics handle @var{h}.
 ##
 ## If @var{filename} has no extension, Octave will try to find the file with
 ## and without the standard extension of @file{.ofig}.
-## @seealso{hgsave, struct2hdl}
+##
+## If provided, the elements of structure @var{prop_struct} will be used to
+## override the properties of top-level object stored in @var{filename} and the
+## old values will be stored in @var{old_prop}.  @var{old_prop} is a cell array
+## matching the size of @var{h}; each cell contains a structure of the existing
+## property names and values before being overriden.
+##
+## @seealso{openfig, hgsave, struct2hdl}
 ## @end deftypefn
 
 ## Author: Massimiliano Fasi
 
-function h = hgload (filename)
+function [h, old_prop] = hgload (filename, prop_struct = struct ())
 
   ## Check input arguments
-  if (nargin != 1)
+  if (nargin == 0 || nargin > 2)
     print_usage ();
   endif
 
+  ## Check second input argument
+  if (! isstruct(prop_struct))
+    error ('hgload: "prop_struct" must be a struct.');
+  endif
+  
   ## Check file existence
   [~, ~, ext] = fileparts (filename);
   if (isempty (ext))
@@ -48,15 +61,32 @@
     endif
   endif
 
-  ## Load the handle
-  try
-    stmp = load (filename, "s_oct40");
-  catch
+  ## Load the handle structure
+  hgs = {"s_oct40", "hgS_050200", "hgS_070000"};
+  hg = load (filename);
+  i = isfield (hg, hgs);
+  if (nnz (i) == 1)
+    hg = hg.(hgs{i});
+  else
     error ("hgload: could not load hgsave-formatted object in file %s", filename);
-  end_try_catch
+  endif
 
-  h = struct2hdl (stmp.s_oct40);
+  ## Override properties of top-level object
+  old_prop = [];
+  fn_new = fieldnames (prop_struct);
+  fn_old = fieldnames (hg.properties);
+  for i = 1:numel (fn_new)
+    idx = ismember (tolower (fn_old), tolower (fn_new{i}));
+    if (any (idx))
+      old_prop.(fn_new{i}) = hg.properties.(fn_old{idx});
+      hg.properties.(fn_old{idx}) = prop_struct.(fn_new{i});
+    endif
+  endfor
+  old_prop = { old_prop };
 
+  ## Build the graphics handle object
+  h = struct2hdl (hg);
+  
 endfunction
 
 
@@ -64,5 +94,28 @@
 
 ## Test input validation
 %!error hgload ()
-%!error hgload (1, 2)
+%!error hgload (1, 2, 3)
+%!error hgload (1, {})
 %!error <unable to locate file> hgload ("%%_A_REALLY_UNLIKELY_FILENAME_%%")
+
+## Test second input and output
+%!test
+%! unwind_protect
+%!   h1 = figure ("Visible", "off");
+%!   col = get (h1, "Color");
+%!   ftmp = [tempname() ".ofig"];
+%!   hgsave (h1, ftmp);
+%!   close (h1);
+%!   [h2, old] = hgload (ftmp);
+%!   assert (old, {[]});
+%!   [h3, old] = hgload (ftmp, struct ("Color", [1 0 0]));
+%!   assert (get (h3, "Color"), [1 0 0]);
+%!   assert (iscell (old) && numel (old) == 1);
+%!   assert (isstruct (old{1}) && isfield (old{1}, "Color"));
+%!   assert (old{1}.Color, col);
+%! unwind_protect_cleanup
+%!   unlink (ftmp);
+%!   try, close (h1); end_try_catch
+%!   try, close (h2); end_try_catch
+%!   try, close (h3); end_try_catch
+%! end_unwind_protect
diff -r df131a44310b -r 6563aa3c4b44 scripts/plot/util/openfig.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/util/openfig.m	Thu Mar 15 15:12:40 2018 +0000
@@ -0,0 +1,129 @@
+## Copyright (C) 2018 Guillaume Flandin
+##
+## This filename 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.
+## 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 filename COPYING.  If not, see
+## <https://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Command} {} openfig
+## @deftypefnx {Function File} openfig (@var{filename})
+## @deftypefnx {Function File} openfig (@dots{}, @var{copies})
+## @deftypefnx {Function File} openfig (@dots{}, @var{visibility})
+## @deftypefnx {Function File} {@var{h} =} openfig (@dots{})
+## Open a saved figure window from @var{filename} and return its graphics handle
+## @var{h}.
+##
+## By default, @var{filename} is @qcode{"Untitled.fig"}.  If a full path is not
+## specified, the file opened will be the first one encountered in the load
+## path.  If @var{filename} is not found and does not have an extension, a
+## search will take place for the first file in the load path with extension
+## @qcode{".fig"} or @qcode{".ofig"}, in that order.
+##
+## @var{copies} is an optional input indicating whether a new figure should
+## always be created (@qcode{"new"}) or if an already opened one can be reused
+## (@qcode{"reuse"}).  Default is @qcode{"new"}.
+##
+## @var{visibility} is an optional input indicating whether to make the figure
+## visible (@qcode{"visible"}) or not (@qcode{"invisible"}).  Default is
+## @qcode{"visible"} unless this setting is stored in @var{filename}, in which
+## case the latter will be used instead.
+##
+## @seealso{open, hgload, struct2hdl}
+## @end deftypefn
+
+function h = openfig (filename = "Untitled.fig", varargin)
+  
+  if (nargin > 3)
+    error ("Too many input arguments.");
+  endif
+  
+  ## Check input filename
+  [d,n,ext] = fileparts (filename);
+  if (isempty (file_in_loadpath (filename)))
+    if (isempty (ext))
+      filename = fullfile (d,[n ".fig"]);
+      if (isempty (file_in_loadpath (filename)))
+        filename = fullfile (d,[n ".ofig"]);
+        if (isempty (file_in_loadpath (filename)))
+          error ("Cannot find file %s.", filename);
+        endif
+      endif
+    else
+      error ("Cannot find file %s.", filename);
+    endif
+  endif
+  filename = file_in_loadpath (filename);
+
+  ## Get optional arguments
+  copies = true;
+  visibility = {};
+  for i = 1:numel (varargin)
+    if (! ischar (varargin{i}))
+      error ("Input argument must be a char array.");
+    endif
+    switch (tolower (varargin{i}))
+      case "reuse"
+        copies = false;
+      case "new"
+        copies = true;
+      case "visible"
+        visibility = {"Visible", "on"};
+      case "invisible"
+        visibility = {"Visible", "off"};
+      otherwise
+        error ("Unknown option '%s'.", varargin{i});
+    endswitch
+  endfor
+
+  ## Reuse an existing figure?
+  if (! copies)
+    h = findobj (allchild (0), "Type", "figure", "FileName", filename);
+    if (! isempty (h))
+      h = h(end);
+      if (! isempty (visibility))
+        set (h, visibility{:});
+      endif
+      # movegui (h, "onscreen"); ## movegui not yet implemented
+      return;
+    endif
+  endif
+  
+  ## Load graphics objects from file
+  prop_struct = cell2struct (visibility(2:2:end), visibility(1:2:end), 2);
+  h = hgload (filename, prop_struct);
+  set (h, "FileName", filename);
+  
+endfunction
+
+%!error <Too many input arguments.> openfig (1, 2, 3, 4)
+%!error openfig ("%%_A_REALLY_UNLIKELY_FILENAME_%%")
+
+%!test
+%! unwind_protect
+%!   h1 = figure ("Visible", "off");
+%!   ftmp = [tempname() ".ofig"];
+%!   hgsave (h1, ftmp);
+%!   close (h1);
+%!   h2 = openfig (ftmp, "new", "invisible");
+%!   h3 = openfig (ftmp, "reuse");
+%!   assert (h2 == h3);
+%!   close (h2);
+%! unwind_protect_cleanup
+%!   unlink (ftmp);
+%!   try, close (h1); end_try_catch
+%!   try, close (h2); end_try_catch
+%!   try, close (h3); end_try_catch
+%! end_unwind_protect
