function A = response(NumRows,NumColumns,AxHandle,ResponseType,SystemNames,InputLabels,OutputLabels)
%RESPONSE Create a Response Object for the Control System Toolbox

%   Authors: Karen Gondoly, 12-31-97
%   Revised: Adam DiVergilio, 2-2000
%   Copyright 1986-2000 The MathWorks, Inc. 
%   $Revision: 1.43 $  $Date: 2000/08/02 16:05:44 $

ni=nargin;
if ni<4
   ResponseType = '';
   InputLabels = {};
   OutputLabels = {};
   SystemNames = {};
end
if ni==0
   NumRows = 0;
   NumColumns = 0;
end

%---Only use colors with a valid color plot symbol
ColorTable8 = rguicolors(8);

%---Properties
A = struct(...
   'ArraySelector',    'off',...
   'AxesGrouping',     'none',...
   'BackgroundAxes',   [],...
   'ChannelSelector',  'off',...
   'ColorOrder',       {ColorTable8(1:7,:)},... % Leave white out
   'Grid',             'off',...
   'InputLabel',       {InputLabels(:)},...
   'LineStyleOrder',   [],...
   'MarkerOrder',      {{'none';'x';'o';'+';'*';'s';'d';'p'}},...
   'NextPlot',         'replace',...
   'OutputLabel',      {OutputLabels(:)},...
   'Parent',           [],...
   'PlotAxes',         [],...
   'ResponseHandles',  [],...
   'ResponseType',     ResponseType,...
   'SelectedChannels', [],...
   'SelectedModels',   [],...
   'SystemNames',      {SystemNames(:)},...
   'SystemVisibility', [],...
   'Title',            '',...
   'UIContextMenu',    [],...
   'Xlabel',           '',...
   'XlimMode',         'auto',...
   'Xlim',             {cell(NumColumns,1)},...
   'Ylabel',           '',...
   'YlimMode',         'auto',...
   'Ylim',             {cell(NumRows,1)},...
   'Zoom',             'off',...
   'RefreshAxisLimits',0,...
   'Preferences',      [],...
   'Handles',          [],...
   'Version',          1);

A.Xlim(:) = {[0 1]}; 
A.Ylim(:) = {[0 1]};

if ni==0
   %---Empty response object
   A = class(A,'response');
   return
elseif ni<3
   %---Get the correct axes to plot to
   AxHandle = newplot;
elseif ~ishandle(AxHandle) | ~strcmp('axes',get(AxHandle,'type'))
   %---Check for invalid axis handle
   error('An invalid axes handle was passed to RESPONSE.');
end
A.BackgroundAxes = AxHandle;
set(AxHandle,'Visible','off',...
   'Xlim',[0 1],'Ylim',[0 1],'XTick',[],'YTick',[],'XTickLabel','','YTickLabel','');

%---Get parent figure and make sure its DoubleBuffer property is on
A.Parent = get(AxHandle,'Parent');
set(A.Parent,'DoubleBuffer','on');

%---Set preferences for this response object
ud = get(A.Parent,'UserData');
if isa(ud,'viewgui')
   %---If we're inside a viewer, use this viewer's preferences
   A.Preferences = get(ud,'Preferences');
else
   %---Otherwise, use the (global) toolbox preferences
   A.Preferences = cstprefs.tbxprefs;
end

%---Inherit linestyle order from parent figure (make sure its a cell array)
A.LineStyleOrder = get(A.Parent,'defaultaxeslinestyleorder');
if ~isa(A.LineStyleOrder,'cell')
   A.LineStyleOrder = cellstr(A.LineStyleOrder);
end

%---Check whether the background axis has a CST grid
%--- (e.g., ngrid + nichols(sys))
L = findall(AxHandle(:),'Tag','CSTgridLines');
delete(L);  % For proper behavior in sgrid + step
if ~isempty(L) & any(strcmp(ResponseType,{'nichols','pzmap','rlocus'}))
   %---Note: This case will circumvent the preference setting !!!
   A.Grid = 'on';
else
   %---Otherwise, use preference
   A.Grid = A.Preferences.Grid;
end

%---Get any axis children...if it is not a Response object, data is already
%---plotted and the Hold is on, copy the children to the DisplayAxes. This is 
%---ONLY valid for SISO plots. For MIMO plots, data must be plotted first.
kids = get(AxHandle,'Children');
L = findobj(kids,'Tag','BackgroundResponseObjectLine');
if ~isempty(L)
   kids(find(kids==L))=[];
end
A.NextPlot = get(AxHandle,'NextPlot');
if ~isempty(kids) & strcmp(A.NextPlot,'add') & (~isequal(NumRows,1) | ~isequal(NumColumns,1))
   error('Only SISO response plots can be placed on Held axes');
end

%---Create BackgroundResponseObjectLine
if isempty(L)
   L=line(0,0,'Parent',AxHandle,'Visible','off',...
      'Tag','BackgroundResponseObjectLine',...
      'DeleteFcn','delresp(gcbo)');
end

%---Create LTI display axes
[A.PlotAxes,A.UIContextMenu] = LocalMakeAxes(NumRows,NumColumns,AxHandle,A.NextPlot,L);

if isa(ud,'viewgui')
   %---If we are in the LTI Viewer, listen for changes in the BackgroundAxes Position
    PROP = findprop(findclass(findpackage('hg'),'axes'),'Position');
    Listeners(1) = handle.listener(AxHandle,PROP,'PropertyPostSet',{@localPlotResize,A.UIContextMenu.Main});
else
   %---Otherwise, listen for changes in the figure position
    PROP = findprop(findclass(findpackage('hg'),'figure'),'Position');
    Listeners(1) = handle.listener(A.Parent,PROP,'PropertyPostSet',{@localPlotResize,A.UIContextMenu.Main});
   %---Also, set figure's windowbuttonmotionfcn to 'hoverfig' if we can
    if isempty(get(A.Parent,'WindowButtonMotionFcn'))
       set(A.Parent,'WindowButtonMotionFcn','hoverfig');
    end
end
A.Handles.Listeners = Listeners;

%---Create Title
A.Handles.Title = text('Parent',AxHandle,...
   'Visible','on','HandleVisibility','off',...
   'String','','Clipping','off',...
   'HorizontalAlignment','center','VerticalAlignment','bottom',...
   'Units','pixels','Rotation',0,'Interpreter','tex',...
   'FontSize',  A.Preferences.TitleFontSize,...
   'FontWeight',A.Preferences.TitleFontWeight,...
   'FontAngle', A.Preferences.TitleFontAngle);

%---Create Xlabel
A.Handles.Xlabel = text('Parent',AxHandle,...
   'Visible','on','HandleVisibility','off',...
   'String','','Clipping','off',...
   'HorizontalAlignment','center','VerticalAlignment','cap',...
   'Units','pixels','Rotation',0,'Interpreter','tex',...
   'FontSize',  A.Preferences.XYLabelsFontSize,...
   'FontWeight',A.Preferences.XYLabelsFontWeight,...
   'FontAngle', A.Preferences.XYLabelsFontAngle);

%---Create Ylabel
A.Handles.Ylabel(1) = text('Parent',AxHandle,...
   'Visible','on','HandleVisibility','off',...
   'String','','Clipping','off',...
   'HorizontalAlignment','center','VerticalAlignment','baseline',...
   'Units','pixels','Rotation',90,'Interpreter','tex',...
   'FontSize',  A.Preferences.XYLabelsFontSize,...
   'FontWeight',A.Preferences.XYLabelsFontWeight,...
   'FontAngle', A.Preferences.XYLabelsFontAngle);

%---Create Ylabel2 (used by SISO bode/margin)
A.Handles.Ylabel(2) = text('Parent',AxHandle,...
   'Visible','on','HandleVisibility','off',...
   'String','','Clipping','off',...
   'HorizontalAlignment','center','VerticalAlignment','baseline',...
   'Units','pixels','Rotation',90,'Interpreter','tex',...
   'FontSize',  A.Preferences.XYLabelsFontSize,...
   'FontWeight',A.Preferences.XYLabelsFontWeight,...
   'FontAngle', A.Preferences.XYLabelsFontAngle);

%---Apply preferences to PlotAxes
set(A.PlotAxes,...
   'XColor',     A.Preferences.AxesForegroundColor,...
   'YColor',     A.Preferences.AxesForegroundColor,...
   'FontSize',   A.Preferences.AxesFontSize,...
   'FontWeight', A.Preferences.AxesFontWeight,...
   'FontAngle',  A.Preferences.AxesFontAngle);

%---Apply preferences to IOLabels
IOLabels = [get(A.PlotAxes(:),'Title');get(A.PlotAxes(:),'XLabel');get(A.PlotAxes(:),'YLabel')];
if iscell(IOLabels)
   IOLabels = cat(1,IOLabels{:});
end
set(IOLabels,...
   'String',     '',...
   'Interpreter','none',...
   'Color',      A.Preferences.AxesForegroundColor,...
   'FontSize',   A.Preferences.IOLabelsFontSize,...
   'FontWeight', A.Preferences.IOLabelsFontWeight,...
   'FontAngle',  A.Preferences.IOLabelsFontAngle);

%---Additional menu settings
set(A.UIContextMenu.GridMenu,'Checked',A.Grid);

%---Store the ContextMenu Handle in the BackgroundResponesObjectLine
%    Useful when using GCR and passing in the BackgroundAxes
set(L,'UserData',A.UIContextMenu.Main);

%---Delete BackgroundAxes kids and copy to LTIdisplayAxes, if necessary
if ~isempty(kids) & strcmp(A.NextPlot,'add') & isequal(NumRows,1) & isequal(NumColumns,1)
   newkids = copyobj(kids,A.PlotAxes);
end
delete(kids);

A = class(A,'response');

%---Store the Response Object in the ContextMenus UserData
builtin('set',A.UIContextMenu.Main,'UserData',A);


%----------------------------Internal Functions------------------------------
%%%%%%%%%%%%%%%%%%%%%
%%% LocalMakeAxes %%%
%%%%%%%%%%%%%%%%%%%%%
function [subax,ContextMenu] = LocalMakeAxes(NumRows,NumColumns,AxHandle,Axhold,L);

FigHandle = get(AxHandle,'Parent');
holdstr = get(AxHandle,'NextPlot');

%---Check the PlotBoxAspectRatio and DataAspectRatio of BackgroundAxes
% Make the children mimic this behavior...needed if the BackgroundAxes
% was made square or equal
DARmode = get(AxHandle,'DataAspectRatioMode');
PBARmode = get(AxHandle,'PlotBoxAspectRatioMode');

%=============================
%---Configure the Context Menu
%=============================
Main = uicontextmenu('Parent',FigHandle,'Tag','ResponseObjectContextMenu');

%---Plot Type menu
%---Determine if lsim/initial plot types should be shown on menu
AllPlotTypes = rguidefs('PlotTypes');
AllPlotNames = rguidefs('PlotNames');
ViewerObj = get(FigHandle,'UserData');
if isa(ViewerObj,'viewgui')
   if isempty(get(ViewerObj,'InputSignal'))
      index = find(strcmpi(AllPlotTypes,'lsim'));
      AllPlotTypes(index,:) = [];
      AllPlotNames(index,:) = [];
   end
   if isempty(get(ViewerObj,'InitialCondition'))
      index = find(strcmpi(AllPlotTypes,'initial'));
      AllPlotTypes(index,:) = [];
      AllPlotNames(index,:) = [];
   end
end
StructArgs = cell(2,length(AllPlotTypes));
Pmain = uimenu(Main,'label',xlate('Plot Type'),'Visible','off'); 
for ctMenu=1:length(AllPlotTypes)
   hMenu = uimenu(Pmain,'label',xlate(AllPlotNames{ctMenu}),'Tag',AllPlotTypes{ctMenu});
   StructArgs(:,ctMenu) = {AllPlotTypes{ctMenu};hMenu};
end
PlotTypes = struct('Main',Pmain,StructArgs{:});

% Systems Menu
Systems = struct('Main',uimenu(Main,'Label',xlate('Systems')),'Names',[]);

% Array Menu
ArrayMenu = uimenu(Main,...
   'Visible','off', ...
   'Label',xlate('Array Selector...'),...
   'Tag','ArraySelector',...
   'Callback','menufcn(''selectmodels'')');

%---Grouping Menu   
GMain = uimenu(Main,'label',xlate('Axis Grouping'),'Separator','on');
GNone = uimenu(GMain,...
   'Label',xlate('None'),...
   'Tag','none',...
   'Checked','on',...
   'Callback','menufcn(''groupcallback'')');
GAll = uimenu(GMain,...
   'Label',xlate('All'),...
   'Tag','all',...
   'Callback','menufcn(''groupcallback'')');
GIn = uimenu(GMain,...
   'Label',xlate('Inputs'),...
   'Tag','inputs',...
   'Callback','menufcn(''groupcallback'')');
GOut = uimenu(GMain,...
   'Label',xlate('Outputs'),...
   'Tag','outputs',...
   'Callback','menufcn(''groupcallback'')');
set(GNone,'UserData',[GAll;GIn;GOut]);         
set(GAll,'UserData',[GNone;GIn;GOut]);
set(GIn,'UserData',[GAll;GNone;GOut]);
set(GOut,'UserData',[GAll;GIn;GNone]);
GroupMenu = struct('Main',GMain,'None',GNone,'All',GAll,'In',GIn,'Out',GOut);

%---Channel Menu  
ChannelMenu = uimenu(Main,...
   'Label',xlate('I/O Selector...'),...
   'Tag','IOSelector',...
   'Callback','menufcn(''selectios'')');

%---Plot Options Menu
PlotOptions = struct(...
   'Main',uimenu(Main,'Label',xlate('Characteristics'),'Separator','on'));

%---Grid Menu
GridMenu = uimenu(Main,...
   'Label',xlate('Grid'),...
   'Callback','menufcn(''gridcallback'')');

%---Zoom Menu
ZMain = uimenu(Main,'Label',xlate('Zoom'));;
ZXY = uimenu(ZMain,...
   'Label',xlate('X-Y'),...
   'Callback','menufcn(''zoomcallback'')');
ZX = uimenu(ZMain,...
   'Label',xlate('In-X'),...
   'Callback','menufcn(''zoomcallback'')');
ZY = uimenu(ZMain,...
   'Label',xlate('In-Y'),...
   'Callback','menufcn(''zoomcallback'')');
ZOut = uimenu(ZMain,...
   'Label',xlate('Out'),...
   'Callback','menufcn(''zoomcallback'')');
set(ZXY,'UserData',[ZX;ZY;ZOut]);
set(ZX,'UserData',[ZY;ZXY;ZOut]);         
set(ZY,'UserData',[ZX;ZXY;ZOut]);
set(ZOut,'UserData',[ZX;ZY;ZXY]);
ZoomMenu = struct('Main',ZMain,'ZoomXY',ZXY,'ZoomX',ZX,'ZoomY',ZY,'ZoomOut',ZOut);

%---Properties Menu
if usejava('MWT')
   PropMenu = uimenu(Main,...
    'Label',xlate('Properties...'),...
    'Separator','on',...
    'Callback','menufcn(''propcallback'')');
else
   PropMenu = [];
end

%---Put ContextMenu together
ContextMenu = struct(...
   'Main',Main,...
   'PlotTypes',PlotTypes,...
   'Systems',Systems,...
   'ArrayMenu',ArrayMenu,...
   'GroupMenu',GroupMenu,...
   'ChannelMenu',ChannelMenu,...
   'PlotOptions',PlotOptions,...
   'GridMenu',GridMenu,...
   'ZoomMenu',ZoomMenu,...
   'PropMenu',PropMenu);

if isequal(NumRows,1) & isequal(NumColumns,1)
   set([ContextMenu.ChannelMenu;ContextMenu.GroupMenu.Main],'Visible','off');
end

%---Create or recycle LTIdisplayAxes

%---Create sub axes
subax = zeros(NumRows,NumColumns);

%---Recycle existing "free" display axes
FreeAxes = findobj(get(FigHandle,'Children'),'flat','Tag','FreeDisplayAxes');
Navail = length(FreeAxes);
Nrequest = NumRows*NumColumns;
if Navail==Nrequest
   % Try to preserve tile ordering
   ud = get(FreeAxes(1),'UserData');
   if isa(ud,'struct') & isfield(ud,'Siblings') & isequal(sort(FreeAxes(:)),sort(ud.Siblings(:)))
      FreeAxes = ud.Siblings(:);
   end
end
subax(1:min(Navail,Nrequest)) = FreeAxes(1:min(Navail,Nrequest));

%---Compute new axis positions
AxesPos = axispos(NumRows,NumColumns,AxHandle);

%---Create new axes if necessary and set their fixed properties
for k=Navail+1:Nrequest
   subax(k) = axes('Parent',FigHandle,...
      'Units','pixels',...
      'Position',AxesPos{k},...
      'Box','on',...
      'Visible','off');
end

%---Conditional axis settings
Extra = {};
if strcmp(PBARmode,'manual')
   Extra = [Extra , {'PlotBoxAspectRatio' , get(AxHandle,'PlotBoxAspectRatio')}];
end
if strcmp(DARmode,'manual')
   Extra = [Extra , {'DataAspectRatio' , get(AxHandle,'DataAspectRatio')}];
end

%---Set variable axis properties
% REVISIT: HG bug: resetting position to same value costs dearly (not robust to round-off in unit convert
set(subax,...
   'Units','pixels',{'Position'},AxesPos(:),...
   'DataAspectRatioMode',DARmode,...
   'NextPlot',Axhold,...
   'PlotBoxAspectRatioMode',PBARmode,...
   'XlimMode','manual','YlimMode','manual',...
   'XtickMode','auto','YtickMode','auto',...
   'Tag','LTIdisplayAxes',...
   'ButtonDownFcn',@localAxesButtonDownFcn,...
   Extra{:});

%---Store LTIdisplayAxes info in axis UserData
udDisp=struct('Position',[],...
   'Placement',[],...
   'Parent',AxHandle,...
   'Siblings',subax);
set(subax,'UIContextMenu',ContextMenu.Main,'UserData',udDisp);

%---Store the Handle of the associated Context Menu in the backgroundline
set(L,'UserData',ContextMenu.Main);


%%%%%%%%%%%%%%%%%%%%%%%%%%
% localAxesButtonDownFcn %
%%%%%%%%%%%%%%%%%%%%%%%%%%
function localAxesButtonDownFcn(eventSrc,eventData)
% ButtonDownFcn for Response plot axes
% eventSrc = axes handle
downtype = get(get(eventSrc,'Parent'),'SelectionType');
UIC = get(eventSrc,'UIContextMenu');
RespObj = get(UIC,'UserData');  % response object associated with plot axes
hPropEdit = cstpropedit;        % handle of (unique) property editor

% Process event
switch downtype
case 'normal'
   if ~isempty(hPropEdit) & hPropEdit.GUI.isVisible
      %---Left-click & property editor open: quick target change
      cstpropedit(RespObj);
   end
%   else
%      %---Left-click & no property editor open: delete all linescan markers in axes
      linescan('deleteall',eventSrc);
%   end
case 'open'
   %---Double-click: open editor
   cstpropedit(RespObj);
end


%%%%%%%%%%%%%%%%%%%
% localPlotResize %
%%%%%%%%%%%%%%%%%%%
function localPlotResize(eventSrc,eventData,UIC)
% Listener callback for background axes resize (used inside of Viewer)
% (OR: Listener callback for figure resize (used outside of Viewer))
posupdate(get(UIC,'UserData'));
