1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
########################################################################
##
## Copyright (C) 2008-2024 The Octave Project Developers
##
## See the file COPYRIGHT.md in the top-level directory of this
## distribution or <https://octave.org/copyright/>.
##
## 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 {} {@var{hlink} =} linkprop (@var{h}, "@var{prop}")
## @deftypefnx {} {@var{hlink} =} linkprop (@var{h}, @{"@var{prop1}", "@var{prop2}", @dots{}@})
## Link graphic object properties, such that a change in one is propagated to
## the others.
##
## The input @var{h} is a vector of graphic handles to link.
##
## @var{prop} may be a string when linking a single property, or a cell array
## of strings for multiple properties. During the linking process all
## properties in @var{prop} will initially be set to the values that exist on
## the first object in the list @var{h}.
##
## The function returns @var{hlink} which is a special object describing the
## link. As long as the reference @var{hlink} exists, the link between graphic
## objects will be active. This means that @var{hlink} must be preserved in
## a workspace variable, a global variable, or otherwise stored using a
## function such as @code{setappdata} or @code{guidata}. To unlink properties,
## execute @code{clear @var{hlink}}.
##
## An example of the use of @code{linkprop} is
##
## @example
## @group
## x = 0:0.1:10;
## subplot (1,2,1);
## h1 = plot (x, sin (x));
## subplot (1,2,2);
## h2 = plot (x, cos (x));
## hlink = linkprop ([h1, h2], @{"color","linestyle"@});
## set (h1, "color", "green");
## set (h2, "linestyle", "--");
## @end group
## @end example
##
## @seealso{linkaxes, addlistener}
## @end deftypefn
function hlink = linkprop (h, prop)
if (nargin != 2)
print_usage ();
endif
if (numel (h) < 2)
error ("linkprop: H must contain at least 2 handles");
elseif (! all (ishghandle (h(:))))
error ("linkprop: invalid graphic handle in input H");
endif
if (ischar (prop))
prop = {prop};
elseif (! iscellstr (prop))
error ("linkprop: PROP must be a string or cell string array");
endif
h = h(:);
## Match all objects to the first one in the list before linking
for j = 1 : numel (prop)
set (h(2:end), prop{j}, get (h(1), prop{j}));
endfor
## FIXME: This file needs to be locked to avoid bug #59439. Remove this
## lock once that bug is properly fixed.
mlock ();
## Add listeners to all objects
for i = 1 : numel (h)
for j = 1 : numel (prop)
addlistener (h(i), prop{j},
{@cb_sync_prop, [h(1:i-1); h(i+1:end)], prop{j}});
endfor
endfor
hlink = onCleanup (@() unlink_linkprop (h, prop));
endfunction
function cb_sync_prop (h, ~, hlist, prop)
persistent recursion = false;
## Don't allow recursion
if (! recursion)
unwind_protect
recursion = true;
set (hlist(ishghandle (hlist)), prop, get (h, prop));
unwind_protect_cleanup
recursion = false;
end_unwind_protect
endif
endfunction
function unlink_linkprop (hlist, prop)
hlist = hlist(ishghandle (hlist));
for i = 1 : numel (hlist)
for j = 1 : numel (prop)
dellistener (hlist(i), prop{j});
endfor
endfor
endfunction
%!demo
%! clf;
%! x = 0:0.1:10;
%! subplot (1,2,1);
%! h1 = plot (x, sin (x), "r");
%! subplot (1,2,2);
%! h2 = plot (x, cos (x), "b");
%!
%! input ("Press <enter> to link plots: ");
%! hlink = linkprop ([h1, h2], {"color", "linestyle"});
%! input ("Press <enter> to change color: ");
%! set (h1, "color", "green");
%! input ("Press <enter> to change linestyle: ");
%! set (h2, "linestyle", "--");
%!test
%! hf1 = figure ("visible", "off");
%! hl1 = plot (1:10, "or");
%! hf2 = figure ("visible", "off");
%! hl2 = plot (1:10, "-*g");
%! hf3 = figure ("visible", "off");
%! hl3 = plot (1:10, "-xb");
%! unwind_protect
%! hlink = linkprop ([hl1, hl2, hl3], {"color", "linestyle"});
%! ## Test initial values taken from first object in list
%! assert (get (hl2, "color"), [1 0 0]);
%! assert (get (hl3, "linestyle"), "none");
%! ## Test linking
%! set (hl2, "color", "b");
%! assert (get (hl1, "color"), [0 0 1]);
%! assert (get (hl3, "color"), [0 0 1]);
%! set (hl3, "linestyle", "--");
%! assert (get (hl1, "linestyle"), "--");
%! assert (get (hl2, "linestyle"), "--");
%! ## Test linking of remaining objects after deletion of one object
%! delete (hl2);
%! set (hl1, "linestyle", ":");
%! assert (get (hl3, "linestyle"), ":");
%! ## Test deletion of link
%! clear hlink;
%! set (hl1, "color", "g");
%! assert (get (hl3, "color"), [0 0 1]);
%! unwind_protect_cleanup
%! close ([hf1 hf2 hf3]);
%! end_unwind_protect
## Test input validation
%!error <Invalid call> linkprop ()
%!error <Invalid call> linkprop (1)
%!error <H must contain at least 2 handles> linkprop (1, "color")
%!error <invalid graphic handle in input H> linkprop ([pi, e], "color")
%!error <PROP must be a string or cell string array> linkprop ([0, 0], 1)
|