function s = edit(Constr,Container)
%EDIT  Builds Bode gain constraint parameter editor.

%   Authors: P. Gahinet, A. Stothert
%   Copyright 1986-2004 The MathWorks, Inc. 
%   $Revision: 1.1.6.3 $ $Date: 2004/12/26 21:51:02 $

import com.mathworks.mwt.*;

% Definitions
Prefs = cstprefs.tbxprefs;
GL_41 = java.awt.GridLayout(4,1,0,3);
LEFT  = MWLabel.LEFT;
CENTER = MWLabel.CENTER;
BWEST = MWBorderLayout.WEST;
BCENTER = MWBorderLayout.CENTER;
BEAST = MWBorderLayout.EAST;

% Labels
P1 = MWPanel(GL_41); 
Container.add(P1,BWEST);
L = cell(4,1);
Text = {xlate('Select edge to edit');...
   xlate('Frequency');...
   xlate('Magnitude');...
   xlate('Slope (dB/decade)')};
for ct=1:4
    Label = MWLabel(sprintf('%s:',Text{ct}),LEFT); 
    P1.add(Label);  
    Label.setFont(Prefs.JavaFontP);
    L{ct} = Label;
end

%Edge Selector
EdgeP  = MWPanel(GL_41);
EdgeCB = MWChoice;
for ct = 1:size(Constr.xCoords,1)
   EdgeCB.addItem(num2str(ct));
end
EdgeP.add(EdgeCB);

 % Text fields
P6 = MWPanel(MWBorderLayout(0,0)); 
Container.add(P6,BCENTER);
P2 = MWPanel(MWBorderLayout(7,0)); 
P6.add(P2,BWEST);
T = cell(4,3);
% Column #1
P3 = MWPanel(GL_41);  P2.add(P3,BWEST);
P3.add(EdgeCB);   T{1,1} = EdgeCB;
W = MWTextField(10); 
P3.add(W);   W.setFont(Prefs.JavaFontP);  T{2,1} = W;
W = MWTextField(10); 
P3.add(W);   W.setFont(Prefs.JavaFontP);  T{3,1} = W;
W = MWTextField(10); 
P3.add(W);   W.setFont(Prefs.JavaFontP);  T{4,1} = W;
% Column #2
P4 = MWPanel(GL_41);   P2.add(P4,BCENTER);
P4.add(MWLabel('',LEFT));
W = MWLabel(sprintf('to'),CENTER); 
P4.add(W);   W.setFont(Prefs.JavaFontP);  T{2,2} = W;
W = MWLabel(sprintf('to'),CENTER); 
P4.add(W);   W.setFont(Prefs.JavaFontP);  T{3,2} = W;
% Column #3
P5 = MWPanel(GL_41);   P2.add(P5,BEAST);
P5.add(MWLabel('',LEFT));
W = MWTextField(10); 
P5.add(W);   W.setFont(Prefs.JavaFontP);  T{2,3} = W;
W = MWTextField(10); 
P5.add(W);   W.setFont(Prefs.JavaFontP);  T{3,3} = W;

% Callbacks
Callback = {@LocalChangeEdge Constr T};
set(T{1,1},'ItemStateChangedCallback',Callback)
Callback = {@LocalEditFrequency Constr T 1};
set(T{2,1},'ActionPerformedCallback',Callback,'FocusLostCallback',Callback)
Callback = {@LocalEditMagnitude Constr T 1};
set(T{3,1},'ActionPerformedCallback',Callback,'FocusLostCallback',Callback)
Callback = {@LocalEditSlope Constr T};
set(T{4,1},'ActionPerformedCallback',Callback,'FocusLostCallback',Callback)
Callback = {@LocalEditFrequency Constr T 2};
set(T{2,3},'ActionPerformedCallback',Callback,'FocusLostCallback',Callback)
Callback = {@LocalEditMagnitude Constr T 2};
set(T{3,3},'ActionPerformedCallback',Callback,'FocusLostCallback',Callback)

% Initialize text field values
LocalUpdateText([],[],Constr,T);

% Update listeners (track changes in constraint data)
props = [Constr.findprop('Frequency');...
      Constr.findprop('Magnitude');...
      Constr.findprop('FrequencyUnits');...
      Constr.findprop('MagnitudeUnits');...
      Constr.findprop('SelectedEdge')];
Listener = handle.listener(Constr,props,'PropertyPostSet',{@LocalUpdateText Constr T});
Listener = [Listener; ...
   handle.listener(Constr,'DataChanged',{@LocalUpdateText Constr T})];

% Save other handles
s = struct('Panels',{{P1;P2;P3;P4;P5;P6}},'Handles',{[L ; T(:)]},'Listeners',Listener);

%--------------------------------------------------------------------------
function LocalChangeEdge(eventsrc,eventdata,Constr,T)

NewEdge = str2double(T{1,1}.getSelectedItem);
if (NewEdge~=Constr.SelectedEdge(1)) && ...
      (NewEdge > 0) && ...
      (NewEdge <= size(Constr.Frequency,1)) 
   Constr.SelectedEdge = NewEdge;
   LocalUpdateText([],[],Constr,T);
end

%--------------------------------------------------------------------------
function LocalUpdateText(eventsrc,eventdata,Constr,T)

% Updates text fields from contraint data
f = unitconv(Constr.Frequency(Constr.SelectedEdge,:),Constr.FrequencyUnits,Constr.getDisplayUnits('XUnits'));
mag = Constr.Magnitude(Constr.SelectedEdge,:);
mag(abs(mag)<1e-3) = 0;
mag = unitconv(mag,Constr.MagnitudeUnits,Constr.getDisplayUnits('YUnits'));
if T{1,1}.getItemCount ~= size(Constr.Frequency,1)
   %Added or deleted edge, need to reset combobox item list
   Callback = get(T{1,1},'ItemStateChangedCallback');
   %Disable combobox callbacks
   set(T{1,1},'ItemStateChangedCallback','')
   T{1,1}.removeAll;
   for ct = 1:size(Constr.Frequency,1)
      T{1,1}.addItem(num2str(ct));
   end
   T{1,1}.select(Constr.SelectedEdge(1)-1);
   set(T{1,1},'ItemStateChangedCallback',Callback)
else
   T{1,1}.select(Constr.SelectedEdge(1)-1);
end

set(T{2,1},'Text',sprintf('%.3g',f(1)))
set(T{2,3},'Text',sprintf('%.3g',f(2)))
set(T{3,1},'Text',sprintf('%.3g',mag(1)))
set(T{3,3},'Text',sprintf('%.3g',mag(2)))
set(T{4,1},'Text',sprintf('%.3g',Constr.slope(Constr.SelectedEdge(1))))

%--------------------------------------------------------------------------
function v = LocalEvaluate(TextField)
% Evaluate text field content
s = get(TextField,'Text');
if isempty(s)
   v = [];
else
   v = evalin('base',s,'[]');
   if ~isequal(size(v),[1 1]) || ~isreal(v),
      v = [];
   end
end

%--------------------------------------------------------------------------
function LocalEditFrequency(TextField,eventData,Constr,T,jx)

% Update min or max frequency
v = unitconv(LocalEvaluate(TextField),Constr.getDisplayUnits('XUnits'),Constr.FrequencyUnits);
s = Constr.slope(Constr.SelectedEdge);  % grab current slope before changes

% Get new frequency range 
FRange = Constr.Frequency(Constr.SelectedEdge,:);
if ~isempty(v) && v>0
   if (jx==1 && v>=FRange(2))
      FRange(2) = v*FRange(2)/FRange(1);  % Adjust upper freq, preserving decade extent
   elseif (jx==2 && v<=FRange(1))
      FRange(1) = v*FRange(1)/FRange(2);
   end
   FRange(jx) = v;
   % Keep left of Nyquist freq
   if Constr.Ts,
      FRange = min([FRange ; (pi/Constr.Ts)*[0.9 1]]);
   end
end

% Update frequency data
if isequal(Constr.Frequency(Constr.SelectedEdge,:),FRange)
   % No change: resync text
   LocalUpdateText([],[],Constr,T);
else
   T = Constr.recordon;
   Constr.Frequency(Constr.SelectedEdge,:) = FRange;
   % Update magnitude (first mag is fixed)
   Constr.Magnitude(Constr.SelectedEdge,:) = Constr.Magnitude(Constr.SelectedEdge,1) + ...
      [0,s*log10(FRange(2)/FRange(1))];
   Constr.recordoff(T);
   % Update display and notify observers
   Constr.send('DataChangeFinished');
   Constr.update
end

%--------------------------------------------------------------------------
function LocalEditMagnitude(TextField,eventData,Constr,T,jx)

% Update start and end magnitudes
v = LocalEvaluate(TextField);
vabs = unitconv(v,Constr.getDisplayUnits('YUnits'),'abs');
if ~isempty(v) && vabs>0,
   v = unitconv(v,Constr.getDisplayUnits('YUnits'),'dB');  % mag in dB
   % Round other mag to nearest feasible value
   jxc = 3-jx;  % complement
   df = log10(Constr.Frequency(Constr.SelectedEdge,jxc)/Constr.Frequency(Constr.SelectedEdge,jx));
   slope = (Constr.Magnitude(Constr.SelectedEdge,jxc)-v)/df;
   NewMag(1,[jxc jx]) = [v+slope*df,v];
   % Update constraint data
   T = Constr.recordon;
   Constr.Magnitude(Constr.SelectedEdge,:) = unitconv(NewMag,'dB',Constr.MagnitudeUnits);
   Constr.recordoff(T);
   
   % Update display and notify observers
   Constr.send('DataChangeFinished');
   Constr.update
else
   LocalUpdateText([],[],Constr,T);
end

%--------------------------------------------------------------------------
function LocalEditSlope(TextField,eventData,Constr,T)
% Update start and end magnitudes
v0 = LocalEvaluate(TextField);
if ~isempty(v0)
   % Update max mag.
   df = log10(Constr.Frequency(Constr.SelectedEdge,2)/Constr.Frequency(Constr.SelectedEdge,1));
   T = Constr.recordon;
   NewMagDB = unitconv(Constr.Magnitude(Constr.SelectedEdge,1),'dB',...
      Constr.MagnitudeUnits) + [0,v0*df];
   Constr.Magnitude(Constr.SelectedEdge,:) = unitconv(NewMagDB,'dB',...
      Constr.MagnitudeUnits);
   Constr.recordoff(T);
   % Update display and notify observers
   Constr.update
else
   LocalUpdateText([],[],Constr,T);
end
