function Main = limit_gui(PropObj,Axis)
%LIMIT_GUI  GUI for editing limit properties of PropObj

%   Author(s): A. DiVergilio
%   Copyright 1986-2000 The MathWorks, Inc. 
%   $Revision: 1.7 $  $Date: 2000/08/02 16:05:52 $

%---Get Toolbox Preferences
Prefs = cstprefs.tbxprefs;

%---Definitions
LCENTER = com.mathworks.mwt.MWLabel.CENTER;
LRIGHT  = com.mathworks.mwt.MWLabel.RIGHT;
LLEFT   = com.mathworks.mwt.MWLabel.LEFT;
BCENTER = com.mathworks.mwt.MWBorderLayout.CENTER;
BWEST   = com.mathworks.mwt.MWBorderLayout.WEST;
BEAST   = com.mathworks.mwt.MWBorderLayout.EAST;
s.isY   = strcmpi(Axis,'Y');

%---Axis-dependent properties
if s.isY
   Label1 = '(Magnitude)';
   Label2 = '(Phase)';
   IOLabel = 'Outputs:';
   GL_N1 = java.awt.GridLayout(4,1,0,4);
else
   Label1 = '';
   IOLabel = 'Inputs:';
   GL_N1 = java.awt.GridLayout(3,1,0,4);
end

%---Top-level panel (MWGroupbox)
Main = com.mathworks.mwt.MWGroupbox(sprintf('%s-Limits',Axis));
Main.setFont(Prefs.JavaFontB);
Main.setLayout(com.mathworks.mwt.MWBorderLayout(8,0));

%---Column 1 (west in Main)
s.C1 = com.mathworks.mwt.MWPanel(GL_N1); Main.add(s.C1,BWEST);
   s.AutoScaleLabel = com.mathworks.mwt.MWLabel(sprintf('Auto-Scale:'),LLEFT); s.C1.add(s.AutoScaleLabel);
   s.AutoScaleLabel.setFont(Prefs.JavaFontP);
   s.IOLabel        = com.mathworks.mwt.MWLabel(sprintf('%s',sprintf(IOLabel)),LRIGHT); s.C1.add(s.IOLabel);
   s.IOLabel.setFont(Prefs.JavaFontP);
   s.LimitsLabel    = com.mathworks.mwt.MWLabel(sprintf('Limits:'),LRIGHT);    s.C1.add(s.LimitsLabel);
   s.LimitsLabel.setFont(Prefs.JavaFontP);

%---Center panel (center in Main, holds s.C2,s.C3)
s.Center = com.mathworks.mwt.MWPanel(com.mathworks.mwt.MWBorderLayout(8,0)); Main.add(s.Center,BCENTER);
   %---Column 2 (west in s.Center)
   s.C2 = com.mathworks.mwt.MWPanel(GL_N1); s.Center.add(s.C2,BWEST);
      s.AutoScalePanel = com.mathworks.mwt.MWPanel(com.mathworks.mwt.MWBorderLayout(8,0)); s.C2.add(s.AutoScalePanel);
         s.AutoScale = com.mathworks.mwt.MWCheckbox; s.AutoScalePanel.add(s.AutoScale,BWEST);
         s.AutoScale.setFont(Prefs.JavaFontP);
      s.IOSelect = com.mathworks.mwt.MWChoice; s.C2.add(s.IOSelect);
         s.IOSelect.setFont(Prefs.JavaFontP);
      s.L1 = com.mathworks.mwt.MWPanel(com.mathworks.mwt.MWBorderLayout(5,0)); s.C2.add(s.L1);
         s.Lim1  = com.mathworks.mwt.MWTextField(7); s.L1.add(s.Lim1,BWEST);
            s.Lim1.setFont(Prefs.JavaFontP);
         s.LimTo = com.mathworks.mwt.MWLabel(sprintf('to'),LCENTER); s.L1.add(s.LimTo,BCENTER);
            s.LimTo.setFont(Prefs.JavaFontP);
         s.Lim2  = com.mathworks.mwt.MWTextField(7); s.L1.add(s.Lim2,BEAST);
            s.Lim2.setFont(Prefs.JavaFontP);
      if s.isY
         s.L2 = com.mathworks.mwt.MWPanel(com.mathworks.mwt.MWBorderLayout(5,0)); s.C2.add(s.L2);
            s.Lim1P  = com.mathworks.mwt.MWTextField(7); s.L2.add(s.Lim1P,BWEST);
               s.Lim1P.setFont(Prefs.JavaFontP);
            s.LimToP = com.mathworks.mwt.MWLabel(sprintf('to'),LCENTER); s.L2.add(s.LimToP,BCENTER);
               s.LimToP.setFont(Prefs.JavaFontP);
            s.Lim2P  = com.mathworks.mwt.MWTextField(7); s.L2.add(s.Lim2P,BEAST);
               s.Lim2P.setFont(Prefs.JavaFontP);
      end
   %---Column 3 (center in s.Center)
   s.C3 = com.mathworks.mwt.MWPanel(GL_N1); s.Center.add(s.C3,BCENTER);
      s.C3L1 = com.mathworks.mwt.MWLabel; s.C3.add(s.C3L1);
         s.C3L1.setFont(Prefs.JavaFontP);
      s.C3L2 = com.mathworks.mwt.MWLabel; s.C3.add(s.C3L2);
         s.C3L2.setFont(Prefs.JavaFontP);
      s.C3L3 = com.mathworks.mwt.MWLabel(sprintf('%s',Label1),LLEFT); s.C3.add(s.C3L3);
         s.C3L3.setFont(Prefs.JavaFontP);
      if s.isY
         s.C3L4 = com.mathworks.mwt.MWLabel(sprintf('%s',Label2),LLEFT); s.C3.add(s.C3L4);
            s.C3L4.setFont(Prefs.JavaFontP);
      end

%---Install PropObj listeners
a = findpackage('cstprefs');
resppropC = a.findclass('respprop');
EventType = 'PropertyPostSet';
PointOfAttachment = PropObj;
Callback = {@localReadProp,s};
GUICallback = {@localWriteProp,PropObj,s};
   %---XlimMode / YlimMode
   Name = [Axis 'limMode'];
   EventSrc = resppropC.findprop(Name);
   s.LimModeListener = handle.listener(PointOfAttachment,EventSrc,EventType,Callback);
   set(s.AutoScale,'Name',Name,'ItemStateChangedCallback',GUICallback);
   %---Xlim / Ylim
   EventSrc = resppropC.findprop([Axis 'lim']);
   s.LimListener = handle.listener(PointOfAttachment,EventSrc,EventType,Callback);
   %---XlimSet / YlimSet
   EventSrc = resppropC.findprop([Axis 'limSet']);
   s.LimSetListener = handle.listener(PointOfAttachment,EventSrc,EventType,Callback);
   set(s.Lim1,'Name',[Axis 'lim1'],...
      'ActionPerformedCallback',GUICallback,'FocusLostCallback',GUICallback);
   set(s.Lim2,'Name',[Axis 'lim2'],...
      'ActionPerformedCallback',GUICallback,'FocusLostCallback',GUICallback);
   if s.isY
      %---ResponseType (Y-Limits only)
      EventSrc = resppropC.findprop('ResponseType');
      s.ResponseTypeListener = handle.listener(PointOfAttachment,EventSrc,EventType,Callback);
      %---OutputLabel (Y-Limits only)
      EventSrc = resppropC.findprop('OutputLabel');
      s.IOLabelListener = handle.listener(PointOfAttachment,EventSrc,EventType,Callback);
      set(s.IOSelect,'Name','OutputLabel','ItemStateChangedCallback',GUICallback);
      %---Phase limits
      set(s.Lim1P,'Name',[Axis 'lim1P'],...
         'ActionPerformedCallback',GUICallback,'FocusLostCallback',GUICallback);
      set(s.Lim2P,'Name',[Axis 'lim2P'],...
         'ActionPerformedCallback',GUICallback,'FocusLostCallback',GUICallback);
   else
      %---InputLabel (X-Limits only)
      EventSrc = resppropC.findprop('InputLabel');
      s.IOLabelListener = handle.listener(PointOfAttachment,EventSrc,EventType,Callback);
      set(s.IOSelect,'Name','InputLabel','ItemStateChangedCallback',GUICallback);
   end

%---Store java handles
set(Main,'UserData',s);


%%%%%%%%%%%%%%%%%
% localReadProp %
%%%%%%%%%%%%%%%%%
function localReadProp(eventSrc,eventData,s)
% Update GUI when property object changes
if eventData.AffectedObject.ListenerEnable
   Name = eventSrc.Name;
   PropObj = eventData.AffectedObject;
   switch Name
   case 'ResponseType'
      isBode = any(strcmpi(eventData.NewValue,{'bode';'margin'}));
      if isBode & isempty(s.L2.getParent)
         %---If is bode-type and phase panel is not shown, add it
         GL = java.awt.GridLayout(s.C2.getComponentCount+1,1,0,4);
         s.C1.setLayout(GL); s.C2.setLayout(GL); s.C3.setLayout(GL);
         s.C2.add(s.L2);    %---Phase limits
         s.C3.add(s.C3L3);  %---Mag label
         s.C3.add(s.C3L4);  %---Phase label
      elseif ~isBode & ~isempty(s.L2.getParent)
         %---ElseIf not bode-type and phase panel is shown, remove it
         s.C3.remove(s.C3L4);  %---Phase label
         s.C3.remove(s.C3L3);  %---Mag label
         s.C2.remove(s.L2);    %---Phase limits
         GL = java.awt.GridLayout(s.C2.getComponentCount,1,0,4);
         s.C1.setLayout(GL); s.C2.setLayout(GL); s.C3.setLayout(GL);
      end

   case {'XlimMode';'YlimMode'}
      s.AutoScale.setState(strcmpi(eventData.NewValue,'auto'));

   case {'Xlim';'Ylim'}
      Lim = get(PropObj,Name);
      isBode = any(strcmpi(PropObj.ResponseType,{'bode';'margin'}));
      NLim = size(Lim,1) / (1+s.isY*isBode);
      if NLim>1 & isempty(s.IOSelect.getParent)
         %---If Mimo, make sure Mimo widgets are showing
         GL = java.awt.GridLayout(s.C2.getComponentCount+1,1,0,4);
         s.C1.setLayout(GL); s.C2.setLayout(GL); s.C3.setLayout(GL);
         s.C1.add(s.IOLabel,1);
         s.C2.add(s.IOSelect,1);
         s.C3.add(s.C3L2,1);
      elseif NLim==1 & ~isempty(s.IOSelect.getParent)
         %---If Siso, make sure Mimo widgets are removed
         s.C1.remove(s.IOLabel);
         s.C2.remove(s.IOSelect);
         s.C3.remove(s.C3L2);
         GL = java.awt.GridLayout(s.C2.getComponentCount,1,0,4);
         s.C1.setLayout(GL); s.C2.setLayout(GL); s.C3.setLayout(GL);
      end
      %---Update limits shown for selected channel
      switch Name
      case 'Xlim'
         localXlimSelect(PropObj,s.IOSelect.getSelectedIndex);
      case 'Ylim'
         localYlimSelect(PropObj,s.IOSelect.getSelectedIndex);
      end

   case {'InputLabel';'OutputLabel'}
      %---Update IO Selector to reflect new IO Labels
      IOLabels = get(PropObj,Name);
      s.IOSelect.removeAll;
      s.IOSelect.addItem(sprintf('all'));
      for n=1:size(IOLabels,1)
         s.IOSelect.addItem(sprintf('%s',IOLabels{n,1}));
      end
      %---Select first IO channel by default and update limit TextFields
      s.IOSelect.select(1);
      switch Name
      case 'InputLabel'
         localXlimSelect(PropObj,1);
      case 'OutputLabel'
         localYlimSelect(PropObj,1);
      end

   case {'XlimSet';'YlimSet'}
      %---Update TextFields to reflect new channel limits
      Lim = get(PropObj,Name);
      if isempty(Lim{1,1})
         s.Lim1.setText('');
         s.Lim2.setText('');
      else
         s.Lim1.setText(num2str(Lim{1,1}(1)));
         s.Lim2.setText(num2str(Lim{1,1}(2)));
      end
      if size(Lim,1)==2
         if isempty(Lim{2,1})
            s.Lim1P.setText('');
            s.Lim2P.setText('');
         else
            s.Lim1P.setText(num2str(Lim{2,1}(1)));
            s.Lim2P.setText(num2str(Lim{2,1}(2)));
         end
      end
   end
end


%%%%%%%%%%%%%%%%%%
% localWriteProp %
%%%%%%%%%%%%%%%%%%
function localWriteProp(eventSrc,eventData,PropObj,s)
% Update property object when GUI changes
PropObj.ListenerEnable = 1;  %---Keep PropObj listener enabled here !!!
Name = get(eventSrc,'Name');
switch Name
case {'XlimMode';'YlimMode'}
   State = get(eventSrc,'State');
   if strcmpi(State,'on')
      set(PropObj,Name,'auto');
   else
      set(PropObj,Name,'manual');
   end
   PropObj.LastChange = Name;

case {'Xlim1';'Xlim2';'Ylim1';'Ylim2';'Ylim1P';'Ylim2P'}
   switch Name
   case {'Xlim1'}
      oldlim = PropObj.XlimSet(1,1);
      val1s = get(eventSrc,'Text');
      val2s = get(s.Lim2,'Text');
      val1 = evalnum(get(eventSrc,'Text'));
      val2 = evalnum(get(s.Lim2,'Text'));
   case {'Xlim2'}
      oldlim = PropObj.XlimSet(1,1);
      val1s = get(s.Lim1,'Text');
      val2s = get(eventSrc,'Text');
      val1 = evalnum(get(s.Lim1,'Text'));
      val2 = evalnum(get(eventSrc,'Text'));
   case {'Ylim1'}
      oldlim = PropObj.YlimSet(1,1);
      val1s = get(eventSrc,'Text');
      val2s = get(s.Lim2,'Text');
      val1 = evalnum(get(eventSrc,'Text'));
      val2 = evalnum(get(s.Lim2,'Text'));
   case {'Ylim2'}
      oldlim = PropObj.YlimSet(1,1);
      val1s = get(s.Lim1,'Text');
      val2s = get(eventSrc,'Text');
      val1 = evalnum(get(s.Lim1,'Text'));
      val2 = evalnum(get(eventSrc,'Text'));
   case {'Ylim1P'}
      oldlim = PropObj.YlimSet(2,1);
      val1s = get(eventSrc,'Text');
      val2s = get(s.Lim2P,'Text');
      val1 = evalnum(get(eventSrc,'Text'));
      val2 = evalnum(get(s.Lim2P,'Text'));
   case {'Ylim2P'}
      oldlim = PropObj.YlimSet(2,1);
      val1s = get(s.Lim1P,'Text');
      val2s = get(eventSrc,'Text');
      val1 = evalnum(get(s.Lim1P,'Text'));
      val2 = evalnum(get(eventSrc,'Text'));
   end
   if isempty(oldlim{1,1})
      oldval1 = [];
      oldval2 = [];
   else
      oldval1 = oldlim{1,1}(1);
      oldval2 = oldlim{1,1}(2);
   end
   %---Compare strings here, not numbers !!!
   if isequal(val1s,num2str(oldval1)) & isequal(val2s,num2str(oldval2))
      %---No change: return
      return;
   elseif isempty(val1) | isempty(val2)
      %---Empty limit: return
      return;
   elseif val1 >= val2
      %---Invalid limit: return
      return;
   else
      newlim = {[val1 val2]};
   end

   switch Name
   case {'Xlim1';'Xlim2'}
      %---Update Xlim
      PropObj.XlimSet = newlim;
      lims = PropObj.Xlim;
      n = PropObj.XlimSetIndex;
      if n>0
         lims(n,1) = newlim;
      else
         lims(:,1) = newlim;
      end
      PropObj.XlimMode = 'manual';
      PropObj.LastChange = 'XlimMode';
      PropObj.Xlim = lims;
      PropObj.LastChange = 'Xlim';
   otherwise
      %---Update Ylim
      limset = PropObj.YlimSet;
      lims = PropObj.Ylim;
      n = PropObj.YlimSetIndex;
      switch Name
      case {'Ylim1';'Ylim2'}
         limset(1,1) = newlim;         
         if n(1)>0
            lims(n(1),1) = newlim;
         else
            lims(1:length(n):end,1) = newlim;
         end
      case {'Ylim1P';'Ylim2P'}
         limset(2,1) = newlim;         
         if n(2)>0
            lims(n(2),1) = newlim;
         else
            lims(2:length(n):end,1) = newlim;
         end
      end
      PropObj.YlimSet = limset;
      PropObj.YlimMode = 'manual';
      PropObj.LastChange = 'YlimMode';
      PropObj.Ylim = lims;
      PropObj.LastChange = 'Ylim';
   end

case {'InputLabel'}
   %---Update displayed limits for selected channel
   localXlimSelect(PropObj,get(eventSrc,'SelectedIndex'));

case {'OutputLabel'}
   %---Update displayed limits for selected channel
   localYlimSelect(PropObj,get(eventSrc,'SelectedIndex'));

end


%%%%%%%%%%%%%%%%%%%
% localXlimSelect %
%%%%%%%%%%%%%%%%%%%
function localXlimSelect(PropObj,n)
% Update displayed limits of PropObj for selected channel n
if n>size(PropObj.Xlim,1) | n<0
   %---Ensure valid channel is selected (this state can occur when changing between response objects)
   n = 1;
end
if n>0
   %---Single channel selected
   PropObj.XlimSet = {[]}; %---Force update (incase invalid limits had been requested and not applied)
   PropObj.XlimSet = PropObj.Xlim(n,1);
elseif isequal(PropObj.Xlim{:,1})
   %---All channels selected AND they all have same limits
   PropObj.XlimSet = {[]}; %---Force update (incase invalid limits had been requested and not applied)
   PropObj.XlimSet = PropObj.Xlim(1,1);
else
   %---All channels selected BUT they don't all have same limits
   PropObj.XlimSet = {[]};
end
%---Record selected index
PropObj.XlimSetIndex = n;


%%%%%%%%%%%%%%%%%%%
% localYlimSelect %
%%%%%%%%%%%%%%%%%%%
function localYlimSelect(PropObj,n)
% Update displayed limits of PropObj for selected channel n
if any(strcmpi(PropObj.ResponseType,{'bode';'margin'}))
   if n>2*size(PropObj.Ylim,1) | n<0
      %---Ensure valid channel is selected (this state can occur when changing between response objects)
      n = 1;
   end
   if n>0
      %---Single channel selected
      PropObj.YlimSet = {[]}; %---Force update (incase invalid limits had been requested and not applied)
      PropObj.YlimSet = PropObj.Ylim((2*n-1):(2*n),1);
   else
      if isequal(PropObj.Ylim{1:2:end-1,1})
         %---All channels selected AND they all have same magnitude limits
         tmp(1,1) = PropObj.Ylim(1,1);
      else
         %---All channels selected BUT they don't all have same magnitude limits
         tmp(1,1) = {[]};
      end
      if isequal(PropObj.Ylim{2:2:end,1})
         %---All channels selected AND they all have same phase limits
         tmp(2,1) = PropObj.Ylim(2,1);
      else
         %---All channels selected BUT they don't all have same phase limits
         tmp(2,1) = {[]};
      end
      PropObj.YlimSet = {[];[]}; %---Force update (incase invalid limits had been requested and not applied)
      PropObj.YlimSet = tmp;
   end
   %---Record selected index
   PropObj.YlimSetIndex = [max(0,2*n-1) 2*n];
else
   if n>size(PropObj.Ylim,1) | n<0
      %---Ensure valid channel is selected (this state can occur when changing between response objects)
      n = 1;
   end
   if n>0
      %---Single channel selected
      PropObj.YlimSet = {[]}; %---Force update (incase invalid limits had been requested and not applied)
      PropObj.YlimSet = PropObj.Ylim(n,1);
   elseif isequal(PropObj.Ylim{:,1})
      %---All channels selected AND they all have same limits
      PropObj.YlimSet = {[]}; %---Force update (incase invalid limits had been requested and not applied)
      PropObj.YlimSet = PropObj.Ylim(1,1);
   else
      %---All channels selected BUT they don't all have same limits
      PropObj.YlimSet = {[]};
   end
   %---Record selected index
   PropObj.YlimSetIndex = n;
end


%%%%%%%%%%%
% evalnum %
%%%%%%%%%%%
function val = evalnum(val)
% Evaluate string val, returning valid real scalar only, empty otherwise
if ~isempty(val)
   val = evalin('base',val,'[]');
   if ~isnumeric(val) | ~(isreal(val) & isfinite(val) & isequal(size(val),[1 1]))
      val = [];
   end
end
