@@ -54,7 +54,24 @@ function append(lh, newStrings)
5454
5555 % Update legend with line object handles & new string array
5656 newlegendstr = [lh .String newStrings ]; % Need to generate this before adding new plot objects
57- lh.PlotChildren = plothandles ;
57+
58+ % Use the union of the parent axes' Children and the legend
59+ % handle's PlotChildren to properly order the legend strings.
60+ % Union(A, B, 'Sorted') will return A concatenated with the
61+ % values of B not in A, so we have the handles associated with
62+ % the existing entries and then the remaining handles in the
63+ % order they are plotted.
64+ lh.PlotChildren = union(lh .PlotChildren , plothandles , ' stable' );
65+
66+ if numel(newlegendstr ) > numel(lh .PlotChildren )
67+ % MATLAB automatically throws out the extra legend entries
68+ % if the number of strings to be added is larger than the
69+ % number of supported graphics objects that are children of
70+ % the parent axes. legend throws a warning in this case and
71+ % we should too
72+ warning(' legtools:append:IgnoringExtraEntries' , ...
73+ ' Ignoring extra legend entries' );
74+ end
5875 lh.String = newlegendstr ;
5976 end
6077
@@ -109,6 +126,21 @@ function remove(lh, remidx)
109126 );
110127 end
111128
129+ % Check remidx for indices greater than the number of legend
130+ % entries and throw them out.
131+ nlegendentries = numel(lh .PlotChildren );
132+ invalididxmask = remidx > nlegendentries ; % Logical test
133+ if any(invalididxmask )
134+ % If we have any invalid entries, remove them and throw a
135+ % warning
136+ remidx(invalididxmask ) = [];
137+ warning(' legtools:remove:InvalidIndex' , ...
138+ ' Removal indices > %u have been ignored' , nlegendentries ...
139+ );
140+ end
141+
142+
143+
112144 if numel(unique(remidx )) == numel(lh .String )
113145 delete(lh );
114146 warning(' legtools:remove:LegendDeleted' , ...
@@ -117,6 +149,7 @@ function remove(lh, remidx)
117149 else
118150 % Check legend entries to be removed for dummy lineseries
119151 % objects and delete them
152+ objtodelete = [];
120153 count = 1 ;
121154 for ii = remidx
122155 % Our dummy lineseries contain a single NaN YData entry
@@ -166,19 +199,36 @@ function adddummy(lh, newStrings, plotParams)
166199 newStrings = legtools .strcheck(' adddummy' , newStrings );
167200
168201 % See if we have a character input for the single addition case
169- % and put it into a cell
202+ % and put it into a cell. Double nest the cells so behavior is
203+ % consistent with a cell array of cells for multiple new dummy
204+ % entries
170205 if ischar(plotParams )
171- plotParams = {{plotParams }};
206+ plotParams = {cellstr(plotParams )};
207+ end
208+
209+ % For the single dummy entry case, make sure each cell of
210+ % plotParams is a cell so behavior is sonsistent with a cell
211+ % array of cells for multiple new dummy entries
212+ if length(newStrings ) == 1
213+ if ~iscell([plotParams{: }])
214+ plotParams = {plotParams };
215+ end
172216 end
173217
174218 % TODO: More plotParams error checking
175219
176220 parentaxes = lh .PlotChildren(1 ).Parent;
221+
222+ washeld = ishold(parentaxes ); % Set a flag for previous hold state ofthe parent axes
177223 hold(parentaxes , ' on' );
178224 for ii = 1 : length(newStrings )
179225 plot(parentaxes , NaN , plotParams{ii }{: }); % Leave input validation up to plot
180226 end
181- hold(parentaxes , ' off' );
227+
228+ if ~washeld
229+ % If parentaxes wasn't previously held, turn hold back off
230+ hold(parentaxes , ' off' );
231+ end
182232
183233 legtools .append(lh , newStrings ); % Add legend entries
184234 end
@@ -216,18 +266,38 @@ function verchk()
216266 function [newString ] = strcheck(src , newString )
217267 % Validate the input strings
218268 if ischar(newString )
219- % Input string is a character array, assume it's a single
220- % string and dump into a cell
221- newString = {newString };
269+ % Input string is a character array, use cellstr to convert
270+ % to a cell array. See the documentation for cellstr for
271+ % its handling of 2D char arrays.
272+ newString = cellstr(newString );
273+ end
274+
275+ if isa(newString , ' string' )
276+ % MATLAB introduced the String data type in R2016b. To
277+ % avoid having to write separate behavior everywhere to
278+ % handle this, convert the String array to a Cell array
279+ newString = cellstr(newString );
222280 end
223281
224282 % Check to see if we now have a cell array
225283 if ~iscell(newString )
226284 msgID = sprintf(' legtools:%s :InvalidLegendString' , src );
227- error(msgID , ...
228- ' Invalid Data Type Passed: %s\n\n Data must be of type(s): %s , %s ' , ...
229- class(newString ), class({}), class(' ' ) ...
230- );
285+
286+ if ~verLessThan(' matlab' , ' 9.1' )
287+ % String data type introduced in MATLAB R2016b so this
288+ % trying to get its class in older versions will error
289+ % out our error
290+ error(msgID , ...
291+ ' Invalid Data Type Passed: %s\n\n Data must be of type: ''%s'' , ''%s'' , or ''%s'' ' , ...
292+ class(newString ), class(cell(1 )), class(char(' ' )), class(string(' ' )) ...
293+ );
294+ else
295+ % Error message for MATLAB versions older than R2016b
296+ error(msgID , ...
297+ ' Invalid Data Type Passed: %s\n\n Data must be of type: ''%s'' or ''%s'' ' , ...
298+ class(newString ), class(cell(1 )), class(char(' ' )) ...
299+ );
300+ end
231301 end
232302
233303 % Check shape of newStrings and make sure it's 1D
@@ -242,9 +312,9 @@ function verchk()
242312 if ~ischar(newString{ii })
243313 msgID = sprintf(' legtools:%s :ConvertingInvalidLegendString' , src );
244314 warning(msgID , ...
245- ' Input legend '' string'' is of type %s , converting to %s ' , ...
246- class(newString{ii }), class(' ' ) ...
247- );
315+ ' Input legend '' string'' is of type %s , converting to %s ' , ...
316+ class(newString{ii }), class(' ' ) ...
317+ );
248318 newString{ii } = num2str(newString{ii });
249319 end
250320 end
0 commit comments