classdef ResponsePlotPanel < handle
    % @ResponsePlotPanelBlk subclass
    
    %   Author(s): R. Chen
    %   Copyright 2009-2014 The MathWorks, Inc.
    
    
    properties
        
        Handles
        Listeners
        
        Platform
        TimeUnit
        FreqUnit
        TimeUnitString
        FreqUnitString
        
        ParentFigure
        BackgroundColor
        PlotTypeContents
        PlotSystemContents
        StabilityContents
        
        TunedController
        BaseController
        
        TunedAxesColor
        TunedAxesIndex
        TunedTableIndex
        BaseAxesColor
        BaseAxesIndex
        BaseTableIndex
        
        ShownTable
        ShownLegend
        RemovedParameterTableColumn
        RemovedMetricTableColumn
        
        FinalTime = []
        
    end
    
    properties(Dependent = true)
        Form
        ViewDOF
    end
    methods
        
        %% ----------
        % Constructor
        %% ----------
        function this = ResponsePlotPanel(s,Data)
            this.Platform = s.Platform;
            %             this.DOF = s.DOF;
            this.TimeUnit = s.TimeUnit;
            [this.TimeUnitString, this.FreqUnitString, this.FreqUnit] = pidtool.utPIDgetUnitString(s.TimeUnit);
            this.ParentFigure = s.ParentFigure;
            this.BackgroundColor = s.BackgroundColor;
            this.TunedAxesIndex = s.TunedAxesIndex;
            this.TunedTableIndex = s.TunedTableIndex;
            this.TunedAxesColor = s.TunedAxesColor;
            this.BaseAxesIndex = s.BaseAxesIndex;
            this.BaseTableIndex = s.BaseTableIndex;
            this.BaseAxesColor = s.BaseAxesColor;
            this.ShownLegend = s.ShownLegend;
            this.ShownTable = s.ShownTable;
            this.setConstants;
            this.build(Data);
        end
        
        function initialize(this,G,tuned,base,showbase)
            % initialize response (must be initialized first)
            this.Handles.PlotTypeComboBoxCOMPONENT.setSelectedIndex(double(isa(G,'frd')));
            this.resetResponse;
            % initialize tuned controller
            this.setTunedController(tuned);
            % initialize base controller if any
            if isempty(base)
                % disable
                this.Handles.ShowBaseCheckBoxCOMPONENT.setSelected(false);
                this.Handles.ShowBaseCheckBoxCOMPONENT.setEnabled(false);
            else
                % enable
                this.Handles.ShowBaseCheckBoxCOMPONENT.setSelected(showbase);
                this.Handles.ShowBaseCheckBoxCOMPONENT.setEnabled(true);
                % initialize baseline controller
                this.setBaseController(base);
            end
            this.showBaseResponse;
            % initialize legend display
            this.showLegend;
            % initialize table display
            this.showTable;
        end
        
        %% ----------
        % GUI: Layout
        %% ----------
        % re-position
        function setPosition(this, Position)
            set(this.Handles.Panel,'Position',Position);
        end
        
        % resize
        function layout(this)
            p = get(this.Handles.Panel,'Position');
            fw = p(3);
            fh = p(4);
            hTopStrip = 4;
            
            if this.ShownTable
                set(this.Handles.AxesPanel,'Position',[1 0 fw-74 fh-hTopStrip]);
            else
                set(this.Handles.AxesPanel,'Position',[1 0 fw-4 fh-hTopStrip]);
            end
            
            % set table panel
            x0 = fw-73;
            y0 = 1.5;
            width = 71;
            height = fh-hTopStrip-2.5;
            set(this.Handles.TablePanelCONTAINER,'Position',[x0 y0 width height]);
            
            % set controls at the top
            set(this.Handles.PlotTypeTextCONTAINER,'Position',[2 fh-hTopStrip 10 2]);
            set(this.Handles.PlotTypeComboBoxCONTAINER,'Position',[13 fh-hTopStrip 20 2]);
            set(this.Handles.PlotSystemTextCONTAINER,'Position',[37 fh-hTopStrip 12 2]);
            set(this.Handles.PlotSystemComboBoxCONTAINER,'Position',[50 fh-hTopStrip 38 2]);
            set(this.Handles.ShowBaseCheckBoxCONTAINER,'Position',[92 fh-hTopStrip 32 2]);
            set(this.Handles.MoreTextCONTAINER,'Position',[fw-33 fh-hTopStrip 24 2]);
            set(this.Handles.MoreButtonCONTAINER,'Position',[fw-8 fh-hTopStrip 5 2]);
        end
        
        %% ----------
        % Controller update
        %% ----------
        % update tuned controller meta information
        function setTunedController(this, s)
            this.updateAxes(this.TunedAxesIndex, s);
            this.updateParameters(this.TunedTableIndex, s.Type, s.DOF, s.P, s.I, s.D, s.N, s.b, s.c);
            if this.ShownTable
                this.updateMetrics(this.TunedTableIndex, s);
            end
            this.TunedController = s;
        end
        
        % update base controller meta information
        function setBaseController(this, s)
            this.updateAxes(this.BaseAxesIndex, s);
            this.updateParameters(this.BaseTableIndex, s.Type, s.DOF, s.P, s.I, s.D, s.N, s.b, s.c);
            if this.ShownTable
                this.updateMetrics(this.BaseTableIndex, s);
            end
            this.BaseController = s;
        end
        
        %% ----------
        % Response update
        %% ----------
        % update axes
        function updateAxes(this, Index, Info)
            % update plot
            hw = ctrlMsgUtils.SuspendWarnings; %#ok<*NASGU>
            hResponses = get(this.Handles.hResponsePlot,'Responses');
            hDataSrc = get(hResponses(Index),'DataSrc');
            PlotSystem = this.PlotSystemContents{this.Handles.PlotSystemComboBoxCOMPONENT.getSelectedIndex+1};
            hDataSrc.Model = Info.(PlotSystem);
            %% Restrict x limit to 20/wc (g778710) note: the following code
            % uses data focus, which is based on the assumption that single
            % siso LTI system is present.  Update x limit only when slider
            % stops moving (e.g. when refreshMode is normal).
            if strcmp(get(hResponses(Index),'refreshMode'),'normal')
                this.refreshXLIM;
            end
        end
        
        function refreshXLIM(this)
            % update X limit for step plot to minimum of {default, 20/WC}
            if strcmp(this.PlotTypeContents{this.Handles.PlotTypeComboBoxCOMPONENT.getSelectedIndex+1},'step');
                % get plot
                hPlot = this.Handles.hResponsePlot;
                % don't use focus in the plot because focus is in manual mode
                hPlot.setfocus([]);
                % get default x limit
                xfocus = hPlot.getfocus;
                xlimDefault = xfocus(end);
                % get desired x limit as 20/wc (plant time unit)
                tuner = get(this.ParentFigure,'UserData');
                xlimNeeded = 20/tuner.CurrentWC;
                % if the default final time is greater, set focus to the new limit
                if xlimDefault > xlimNeeded
                    hPlot.setfocus([0 xlimNeeded],tuner.DataSrc.TimeUnit);
                    this.FinalTime = xlimNeeded;
                    hPlot.Responses(this.TunedAxesIndex).Data.clear;
                    hPlot.Responses(this.TunedAxesIndex).draw;
                    this.FinalTime = [];
                else
                    % update view
                    hPlot.AxesGrid.send('ViewChanged');
                end
            end
        end
        
        %% ----------
        % GUI: Table update
        %% ----------
        % update metric table
        function updateMetrics(this, Index, Info)
            % update performance table
            col = Index+1;
            PlotSystem = this.PlotSystemContents{this.Handles.PlotSystemComboBoxCOMPONENT.getSelectedIndex+1};
            CLsys = Info.(PlotSystem);
            s = pidtool.utPIDgetmetrics(Info.OLsys,CLsys);
            Data = cell(this.Handles.MetricTableModel.data);
            Data{1,col} = sprintf('%0.3g %s',s.RiseTime,this.TimeUnitString);
            Data{2,col} = sprintf('%0.3g %s',s.SettlingTime,this.TimeUnitString);
            Data{3,col} = sprintf('%0.3g %s',s.Overshoot,'%');
            Data{4,col} = sprintf('%0.3g',s.Peak);
            Data{5,col} = sprintf('%0.3g dB @ %0.3g %s',20*log10(s.GainMargin),s.GainMarginAt,this.FreqUnitString);
            Data{6,col} = sprintf('%0.3g deg @ %0.3g %s',s.PhaseMargin,s.PhaseMarginAt,this.FreqUnitString);
            Data{7,col} = this.StabilityContents{Info.IsStable+1};
            this.Handles.MetricTableModel.setData(Data);
        end
        
        % update parameter table
        function updateParameters(this, Index, Type, DOF, P, I, D, N, b, c)
            % update parameter table
            col = Index+1;
            Data = cell(this.Handles.ParameterTableModel.data);
            is2DOFTable = size(Data,1) == 6;
            switch Type
                case 'p'
                    Data{1,col} = num2str(P);
                    Data{2,col} = ' ';
                    Data{3,col} = ' ';
                    Data{4,col} = ' ';
                case 'i'
                    Data{1,col} = ' ';
                    Data{2,col} = num2str(I);
                    Data{3,col} = ' ';
                    Data{4,col} = ' ';
                case 'pi'
                    Data{1,col} = num2str(P);
                    Data{2,col} = num2str(I);
                    Data{3,col} = ' ';
                    Data{4,col} = ' ';
                case 'pd'
                    Data{1,col} = num2str(P);
                    Data{2,col} = ' ';
                    Data{3,col} = num2str(D);
                    Data{4,col} = ' ';
                case 'pdf'
                    Data{1,col} = num2str(P);
                    Data{2,col} = ' ';
                    Data{3,col} = num2str(D);
                    Data{4,col} = num2str(N);
                case 'pid'
                    Data{1,col} = num2str(P);
                    Data{2,col} = num2str(I);
                    Data{3,col} = num2str(D);
                    Data{4,col} = ' ';
                case 'pidf'
                    Data{1,col} = num2str(P);
                    Data{2,col} = num2str(I);
                    Data{3,col} = num2str(D);
                    Data{4,col} = num2str(N);
                case 'pi2'
                    Data{1,col} = num2str(P);
                    Data{2,col} = num2str(I);
                    Data{3,col} = ' ';
                    Data{4,col} = ' ';
                    Data{5,col} = num2str(b);
                    Data{6,col} = ' ';
                case 'pd2'
                    Data{1,col} = num2str(P);
                    Data{2,col} = ' ';
                    Data{3,col} = num2str(D);
                    Data{4,col} = ' ';
                    Data{5,col} = num2str(b);
                    Data{6,col} = num2str(c);
                case 'pdf2'
                    Data{1,col} = num2str(P);
                    Data{2,col} = ' ';
                    Data{3,col} = num2str(D);
                    Data{4,col} = num2str(N);
                    Data{5,col} = num2str(b);
                    Data{6,col} = num2str(c);
                case 'pid2'
                    Data{1,col} = num2str(P);
                    Data{2,col} = num2str(I);
                    Data{3,col} = num2str(D);
                    Data{4,col} = ' ';
                    Data{5,col} = num2str(b);
                    Data{6,col} = num2str(c);
                case 'pidf2'
                    Data{1,col} = num2str(P);
                    Data{2,col} = num2str(I);
                    Data{3,col} = num2str(D);
                    Data{4,col} = num2str(N);
                    Data{5,col} = num2str(b);
                    Data{6,col} = num2str(c);
            end
            if is2DOFTable && DOF == 1
                Data{5,col} = ' ';
                Data{6,col} = ' ';
            end
            this.Handles.ParameterTable.setPreferredScrollableViewportSize(java.awt.Dimension(100,size(Data,1)*18));
            
            this.Handles.ParameterTableModel.setData(Data);
        end
        
        % update parameter table
        function updateGainNames(this)
            
            % update parameter table
            Data = cell(this.Handles.ParameterTableModel.data);
            if strcmp(this.Form,'parallel');
                Data(1,1) = {'Kp'};
                Data(2,1) = {'Ki'};
                Data(3,1) = {'Kd'};
                Data(4,1) = {'Tf'};
            else
                Data(1,1) = {'Kp'};
                Data(2,1) = {'Ti'};
                Data(3,1) = {'Td'};
                Data(4,1) = {'N'};
            end
            if this.ViewDOF == 1
                Data = Data(1:4,:);
            else
                Data(5,1) = {'b'};
                Data(6,1) = {'c'};
            end
            
            this.Handles.ParameterTable.setPreferredScrollableViewportSize(java.awt.Dimension(100,size(Data,1)*18));
            this.Handles.ParameterTableModel.setData(Data);
        end
        %% ----------
        % GUI: Display
        %% ----------
        % set visibility of legend
        function showLegend(this, val)
            % if there is no true/false specified, simply refresh legend
            if nargin == 2
                this.ShownLegend = val;
            end
            this.setDisplayLegend;
        end
        
        % set visibility of baseline response
        function showBaseResponse(this)
            hw = ctrlMsgUtils.SuspendWarnings; %#ok<*NASGU>
            hgroup = handle(this.Handles.hResponsePlot.Responses(this.BaseAxesIndex).Group);
            if this.Handles.ShowBaseCheckBoxCOMPONENT.isSelected
                % display baseline in legend
                for ct = 1:length(hgroup)
                    hgroup(ct).Annotation.LegendInformation.IconDisplayStyle = 'on';
                end
                % display baseline curve
                set(this.Handles.hResponsePlot.Responses(this.BaseAxesIndex),'visible','on');
                % display column in parameter table if necessary
                if this.Handles.ParameterTable.getColumnModel.getColumnCount < this.BaseTableIndex+1
                    this.Handles.ParameterTable.addColumn(this.RemovedParameterTableColumn);
                    this.RemovedParameterTableColumn = [];
                end
                % display column in metrics table if necessary
                if this.Handles.MetricTable.getColumnModel.getColumnCount < this.BaseTableIndex+1
                    this.Handles.MetricTable.addColumn(this.RemovedMetricTableColumn);
                    this.RemovedMetricTableColumn = [];
                end
            else
                % hide baseline in legend
                for ct = 1:length(hgroup)
                    hgroup(ct).Annotation.LegendInformation.IconDisplayStyle = 'off';
                end
                % hide baseline curve
                set(this.Handles.hResponsePlot.Responses(this.BaseAxesIndex),'visible','off');
                % hide column in parameter table if necessary
                if this.Handles.ParameterTable.getColumnModel.getColumnCount == this.BaseTableIndex+1
                    this.RemovedParameterTableColumn = this.Handles.ParameterTable.getColumnModel.getColumn(this.BaseTableIndex);
                    this.Handles.ParameterTable.removeColumn(this.RemovedParameterTableColumn);
                end
                % hide column in metrics table if necessary
                if this.Handles.MetricTable.getColumnModel.getColumnCount == this.BaseTableIndex+1
                    this.RemovedMetricTableColumn = this.Handles.MetricTable.getColumnModel.getColumn(this.BaseTableIndex);
                    this.Handles.MetricTable.removeColumn(this.RemovedMetricTableColumn);
                end
            end
        end
        
        %% ----------
        % GUI: add GUI listeners
        %% ----------
        function addGUIListeners(this)
            h = handle(this.Handles.PlotTypeComboBoxCOMPONENT);
            this.Listeners = handle.listener(h, 'ActionPerformed',{@(x,y) clickPlotTypeComboBox(this)});
            h = handle(this.Handles.PlotSystemComboBoxCOMPONENT);
            this.Listeners(2) = handle.listener(h, 'ActionPerformed',{@(x,y) clickPlotSystemComboBox(this)});
            h = handle(this.Handles.ShowBaseCheckBoxCOMPONENT);
            this.Listeners(3) = handle.listener(h, 'ActionPerformed',{@(x,y) clickShowBaseCheckBox(this)});
            h = handle(this.Handles.MoreButtonCOMPONENT);
            this.Listeners(4) = handle.listener(h, 'ActionPerformed',{@(x,y) clickMoreButton(this)});
        end
        
    end
    
    methods (Hidden)
        
        function qeClickMoreButton(this)
            % internal function for test only!
            this.clickMoreButton;
        end
        
    end
    
    methods (Access = protected)
        
        %% ----------
        % build GUI
        %% ----------
        function build(this, ParameterData)
            
            % main panel
            Panel = uipanel('parent',this.ParentFigure,'BackgroundColor', this.BackgroundColor);
            set(Panel,'units','character','BorderType','beveledout','BorderWidth',2);
            % 1-1: type text
            PlotTypeText = javaObjectEDT('com.mathworks.mwswing.MJLabel',pidtool.utPIDgetStrings('cst','plotpanel_plottype'));
            %PlotTypeText.setBorder(javax.swing.BorderFactory.createLineBorder(java.awt.Color.black))
            PlotTypeText.setName('PIDTUNER_PLOTTYPELABEL');
            PlotTypeText.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            pidtool.utPIDaddCSH('control',PlotTypeText,'pidtuner_plottype');
            [PlotTypeTextCOMPONENT, PlotTypeTextCONTAINER] = matlab.ui.internal.JavaMigrationTools.suppressedJavaComponent(PlotTypeText,[.1,.1,.9,.9],Panel);
            set(PlotTypeTextCONTAINER,'units','character')
            % 1-2: type combobox
            PlotTypeComboBox = javaObjectEDT('com.mathworks.mwswing.MJComboBox',pidtool.utPIDgetStrings('cst','plotpanel_typecombo',2));
            PlotTypeComboBox.setName('PIDTUNER_PLOTTYPECOMBOBOX');
            [PlotTypeComboBoxCOMPONENT, PlotTypeComboBoxCONTAINER] = matlab.ui.internal.JavaMigrationTools.suppressedJavaComponent(PlotTypeComboBox,[.1,.1,.9,.9],Panel);
            set(PlotTypeComboBoxCONTAINER,'units','character')
            % 1-1: type text
            PlotSystemText = javaObjectEDT('com.mathworks.mwswing.MJLabel',pidtool.utPIDgetStrings('cst','plotpanel_plotsystem'));
            %PlotSystemText.setBorder(javax.swing.BorderFactory.createLineBorder(java.awt.Color.black))
            PlotSystemText.setName('PIDTUNER_PLOTSYSTEMLABEL');
            PlotSystemText.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            pidtool.utPIDaddCSH('control',PlotSystemText,'pidtuner_plotsystem');
            [PlotSystemTextCOMPONENT, PlotSystemTextCONTAINER] = matlab.ui.internal.JavaMigrationTools.suppressedJavaComponent(PlotSystemText,[.1,.1,.9,.9],Panel);
            set(PlotSystemTextCONTAINER,'units','character')
            % 1-2: type combobox
            PlotSystemComboBox = javaObjectEDT('com.mathworks.mwswing.MJComboBox',pidtool.utPIDgetStrings('cst','plotpanel_systemcombo',6));
            PlotSystemComboBox.setName('PIDTUNER_PLOTSYSTEMCOMBOBOX');
            [PlotSystemComboBoxCOMPONENT, PlotSystemComboBoxCONTAINER] = matlab.ui.internal.JavaMigrationTools.suppressedJavaComponent(PlotSystemComboBox,[.1,.1,.9,.9],Panel);
            set(PlotSystemComboBoxCONTAINER,'units','character')
            % 1-3: view checkbox (selected by default)
            ShowBaseCheckBox = javaObjectEDT('com.mathworks.mwswing.MJCheckBox',...
                pidtool.utPIDgetStrings(this.Platform,'tunerdlg_showbase'),true);
            ShowBaseCheckBox.setName('PIDTUNER_SHOWBLOCKCHECKBOX');
            pidtool.utPIDaddCSH(this.Platform,ShowBaseCheckBox,'pidtuner_comparecheckbox');
            [ShowBaseCheckBoxCOMPONENT, ShowBaseCheckBoxCONTAINER] = matlab.ui.internal.JavaMigrationTools.suppressedJavaComponent(ShowBaseCheckBox,[.1,.1,.9,.9],Panel);
            set(ShowBaseCheckBoxCONTAINER,'units','character')
            % 1-4: more text
            MoreText = javaObjectEDT('com.mathworks.mwswing.MJLabel');
            %MoreText.setBorder(javax.swing.BorderFactory.createLineBorder(java.awt.Color.black))
            MoreText.setName('PIDTUNER_MORETEXT');
            pidtool.utPIDaddCSH('control',MoreText,'pidtuner_showtable');
            MoreText.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
            [MoreTextCOMPONENT, MoreTextCONTAINER] = matlab.ui.internal.JavaMigrationTools.suppressedJavaComponent(MoreText,[.1,.1,.9,.9],Panel);
            set(MoreTextCONTAINER,'units','character')
            % 1-5: show more button
            MoreButton = javaObjectEDT('com.mathworks.mwswing.MJButton');
            MoreButton.setName('PIDTUNER_MOREBUTTON');
            MoreButton.setFlyOverAppearance(true);
            MoreButton.setFocusTraversable(false);
            [MoreButtonCOMPONENT, MoreButtonCONTAINER] = matlab.ui.internal.JavaMigrationTools.suppressedJavaComponent(MoreButton,[.1,.1,.9,.9],Panel);
            set(MoreButtonCONTAINER,'units','character')
            
            % 2: axes
            AxesPanel = uipanel('parent',Panel,'tag','PIDTUNER_AXESPANEL','Units','characters','BorderType','none');
            Axes = axes('parent',AxesPanel,'tag','PIDTUNER_AXES','Position',[0.06 0.14 0.90 0.84]);
            
            % 3: tables
            Prefs = cstprefs.tbxprefs;
            
            % box
            ParameterTablePanel = javaObjectEDT('com.mathworks.mwswing.MJPanel');
            titleborder = javaMethodEDT('createTitledBorder','javax.swing.BorderFactory',pidtool.utPIDgetStrings('cst','plotpanel_parametertablebox'));
            javaObjectEDT(titleborder);
            ParameterTablePanel.setBorder(titleborder),
            ParameterTablePanel.setLayout(java.awt.BorderLayout(0,0));
            ParameterTablePanel.setFont(Prefs.JavaFontB);
            % table model
            str1 = pidtool.utPIDgetStrings(this.Platform,'plotpanel_tunedtitle');
            str2 = pidtool.utPIDgetStrings(this.Platform,'plotpanel_basetitle');
            ParameterTableModel = javaObjectEDT('com.mathworks.toolbox.control.tableclasses.PIDTunerTableModel',ParameterData,{'',str1,str2},-1);
            % table
            ParameterTable = javaObjectEDT('com.mathworks.mwswing.MJTable',ParameterTableModel);
            ParameterTable.setName('PIDTUNER_PARAMETERTABLE');
            ParameterTable.setRowSelectionAllowed(false);
            % change tuned column renderer
            tmp = javaObjectEDT('javax.swing.table.DefaultTableCellRenderer');
            tmp.setForeground(java.awt.Color(this.TunedAxesColor(1),this.TunedAxesColor(2),this.TunedAxesColor(3)));
            tmp.setBackground(java.awt.Color(this.BackgroundColor(1),this.BackgroundColor(2),this.BackgroundColor(3)));
            ParameterTable.getColumnModel.getColumn(this.TunedTableIndex).setCellRenderer(tmp);
            % change baseline column renderer
            tmp = javaObjectEDT('javax.swing.table.DefaultTableCellRenderer');
            tmp.setForeground(java.awt.Color(0,0,0));
            tmp.setBackground(java.awt.Color(this.BackgroundColor(1),this.BackgroundColor(2),this.BackgroundColor(3)));
            ParameterTable.getColumnModel.getColumn(this.BaseTableIndex).setCellRenderer(tmp);
            % change head column renderer
            tmp = javaObjectEDT('javax.swing.table.DefaultTableCellRenderer');
            tmp.setBackground(java.awt.Color(this.BackgroundColor(1),this.BackgroundColor(2),this.BackgroundColor(3)));
            ParameterTable.getColumnModel.getColumn(0).setCellRenderer(tmp);
            % scroll size
            ParameterTable.setPreferredScrollableViewportSize(java.awt.Dimension(100,size(ParameterData,1)*18))
            % default width
            ParameterTable.getColumnModel.getColumn(0).setPreferredWidth(80);
            % hide grid
            ParameterTable.setGridColor(java.awt.Color(this.BackgroundColor(1),this.BackgroundColor(2),this.BackgroundColor(3)));
            % add to scroll pane
            ParameterTableScrollPane = javaObjectEDT('com.mathworks.mwswing.MJScrollPane',ParameterTable);
            ParameterTablePanel.add(ParameterTableScrollPane,java.awt.BorderLayout.NORTH);
            
            % box
            MetricTablePanel = javaObjectEDT('com.mathworks.mwswing.MJPanel');
            titleborder = javaMethodEDT('createTitledBorder','javax.swing.BorderFactory',pidtool.utPIDgetStrings('cst','plotpanel_metrictablebox'));
            javaObjectEDT(titleborder);
            MetricTablePanel.setBorder(titleborder),
            MetricTablePanel.setLayout(java.awt.BorderLayout(0,0));
            MetricTablePanel.setFont(Prefs.JavaFontB);
            % table model
            tmp = blanks(4);
            Data = {pidtool.utPIDgetStrings('cst','plotpanel_metric1'),tmp,tmp;...
                pidtool.utPIDgetStrings('cst','plotpanel_metric2'),tmp,tmp;...
                pidtool.utPIDgetStrings('cst','plotpanel_metric3'),tmp,tmp;...
                pidtool.utPIDgetStrings('cst','plotpanel_metric4'),tmp,tmp;...
                pidtool.utPIDgetStrings('cst','plotpanel_metric5'),tmp,tmp;...
                pidtool.utPIDgetStrings('cst','plotpanel_metric6'),tmp,tmp;...
                pidtool.utPIDgetStrings('cst','plotpanel_metric7'),tmp,tmp};
            str1 = pidtool.utPIDgetStrings(this.Platform,'plotpanel_tunedtitle');
            str2 = pidtool.utPIDgetStrings(this.Platform,'plotpanel_basetitle');
            MetricTableModel = javaObjectEDT('com.mathworks.toolbox.control.tableclasses.PIDTunerTableModel',Data,{'',str1,str2},-1);
            % table
            MetricTable = javaObjectEDT('com.mathworks.mwswing.MJTable',MetricTableModel);
            MetricTable.setName('PIDTUNER_METRICTABLE');
            MetricTable.setRowSelectionAllowed(false);
            % change tuned column renderer
            tmp = javaObjectEDT('javax.swing.table.DefaultTableCellRenderer');
            tmp.setForeground(java.awt.Color(this.TunedAxesColor(1),this.TunedAxesColor(2),this.TunedAxesColor(3)));
            tmp.setBackground(java.awt.Color(this.BackgroundColor(1),this.BackgroundColor(2),this.BackgroundColor(3)));
            MetricTable.getColumnModel.getColumn(this.TunedTableIndex).setCellRenderer(tmp);
            % change baseline column renderer
            tmp = javaObjectEDT('javax.swing.table.DefaultTableCellRenderer');
            tmp.setForeground(java.awt.Color(0,0,0));
            tmp.setBackground(java.awt.Color(this.BackgroundColor(1),this.BackgroundColor(2),this.BackgroundColor(3)));
            MetricTable.getColumnModel.getColumn(this.BaseTableIndex).setCellRenderer(tmp);
            % change head column renderer
            tmp = javaObjectEDT('javax.swing.table.DefaultTableCellRenderer');
            tmp.setBackground(java.awt.Color(this.BackgroundColor(1),this.BackgroundColor(2),this.BackgroundColor(3)));
            MetricTable.getColumnModel.getColumn(0).setCellRenderer(tmp);
            % default width
            MetricTable.getColumnModel().getColumn(0).setPreferredWidth(80);
            % hide grid
            MetricTable.setGridColor(java.awt.Color(this.BackgroundColor(1),this.BackgroundColor(2),this.BackgroundColor(3)));
            % scroll size
            MetricTableScrollPane = javaObjectEDT('com.mathworks.mwswing.MJScrollPane',MetricTable);
            MetricTablePanel.add(MetricTableScrollPane,java.awt.BorderLayout.CENTER);
            
            TablePanel = javaObjectEDT('com.mathworks.mwswing.MJPanel');
            TablePanel.setLayout(java.awt.BorderLayout(0,15));
            TablePanel.setFont(Prefs.JavaFontB);
            TablePanel.add(ParameterTablePanel,java.awt.BorderLayout.NORTH);
            TablePanel.add(MetricTablePanel,java.awt.BorderLayout.CENTER);
            [TablePanelCOMPONENT, TablePanelCONTAINER] = matlab.ui.internal.JavaMigrationTools.suppressedJavaComponent(TablePanel,[.1,.1,.9,.9],Panel);
            set(TablePanelCONTAINER,'units','character')
            
            this.Handles.Panel = Panel;
            this.Handles.PlotTypeTextCOMPONENT = PlotTypeTextCOMPONENT;
            this.Handles.PlotTypeTextCONTAINER = PlotTypeTextCONTAINER';
            this.Handles.PlotTypeComboBoxCOMPONENT = PlotTypeComboBoxCOMPONENT;
            this.Handles.PlotTypeComboBoxCONTAINER = PlotTypeComboBoxCONTAINER';
            this.Handles.PlotSystemTextCOMPONENT = PlotSystemTextCOMPONENT;
            this.Handles.PlotSystemTextCONTAINER = PlotSystemTextCONTAINER';
            this.Handles.PlotSystemComboBoxCOMPONENT = PlotSystemComboBoxCOMPONENT;
            this.Handles.PlotSystemComboBoxCONTAINER = PlotSystemComboBoxCONTAINER';
            this.Handles.ShowBaseCheckBoxCOMPONENT = ShowBaseCheckBoxCOMPONENT;
            this.Handles.ShowBaseCheckBoxCONTAINER = ShowBaseCheckBoxCONTAINER';
            this.Handles.MoreTextCOMPONENT = MoreTextCOMPONENT;
            this.Handles.MoreTextCONTAINER = MoreTextCONTAINER';
            this.Handles.MoreButtonCOMPONENT = MoreButtonCOMPONENT;
            this.Handles.MoreButtonCONTAINER = MoreButtonCONTAINER';
            this.Handles.Axes = Axes;
            this.Handles.AxesPanel = AxesPanel;
            this.Handles.ParameterTableModel = ParameterTableModel;
            this.Handles.ParameterTable = ParameterTable;
            this.Handles.MetricTableModel = MetricTableModel;
            this.Handles.MetricTable = MetricTable;
            this.Handles.TablePanelCOMPONENT = TablePanelCOMPONENT;
            this.Handles.TablePanelCONTAINER = TablePanelCONTAINER;
        end
        
        function setConstants(this)
            this.PlotTypeContents = {'step','bode'};
            this.PlotSystemContents = {'r2y','r2u','id2y','od2y','OLsys','Plant'};
            StableStr = pidtool.utPIDgetStrings('cst','tunerdlg_stable');
            UnstableStr = pidtool.utPIDgetStrings('cst','tunerdlg_unstable');
            this.StabilityContents = {UnstableStr,StableStr};
        end
        
        %% ----------
        % GUI actions
        %% ----------
        function clickMoreButton(this)
            this.ShownTable = ~this.ShownTable;
            tablesize = get(this.Handles.TablePanelCONTAINER,'position');
            if this.ShownTable
                % resize figure
                POS = get(this.ParentFigure,'Position');
                POS(3) = POS(3) + 70;
                POSinPixels = hgconvertunits(this.ParentFigure, POS, 'character', 'pixels', this.Handles.Panel);
                % relocate figure window if it goes out of bound
                scrsz = get(0,'ScreenSize');
                if POSinPixels(1)+POSinPixels(3)>scrsz(3);
                    NewLeftPosition = scrsz(3)-POSinPixels(3);
                    if NewLeftPosition<0
                        % full screen
                        POSinPixels(1) = 2;
                        POSinPixels(3) = scrsz(3)-4;
                    else
                        POSinPixels(1) = NewLeftPosition-2;
                    end
                end
                POS = hgconvertunits(this.ParentFigure, POSinPixels, 'pixels', 'character', this.Handles.Panel);
                set(this.ParentFigure,'Position',POS);
                drawnow;
                % update metric table because it is not updated when closed
                this.updateMetrics(this.TunedTableIndex, this.TunedController);
                if ~isempty(this.BaseController)
                    this.updateMetrics(this.BaseTableIndex, this.BaseController);
                end
                % show table
                this.showTable;
            else
                % hide table
                this.showTable;
                % resize figure
                tmp = get(this.ParentFigure,'Position');
                tmp(3) = tmp(3) - 70;
                set(this.ParentFigure,'Position',tmp);
            end
        end
        
        function clickPlotTypeComboBox(this)
            this.resetResponse;
            this.clickPlotSystemComboBox;
        end
        
        function clickPlotSystemComboBox(this)
            if this.Handles.ShowBaseCheckBoxCOMPONENT.isSelected
                set(this.Handles.hResponsePlot.Responses(this.BaseAxesIndex),'visible','on');
            else
                set(this.Handles.hResponsePlot.Responses(this.BaseAxesIndex),'visible','off');
            end
            % plot response
            this.updateAxes(this.TunedAxesIndex, this.TunedController);
            if this.ShownTable
                this.updateMetrics(this.TunedTableIndex, this.TunedController);
            end
            if ~isempty(this.BaseController)
                this.updateAxes(this.BaseAxesIndex, this.BaseController);
                if this.ShownTable
                    this.updateMetrics(this.BaseTableIndex, this.BaseController);
                end
            end
            % reset legend
            this.setDisplayLegend;
        end
        
        function clickShowBaseCheckBox(this)
            this.showBaseResponse;
            this.updateGainNames;
        end
        
        % others
        function resetResponse(this)
            PlotType = this.PlotTypeContents{this.Handles.PlotTypeComboBoxCOMPONENT.getSelectedIndex+1};
            switch PlotType
                case 'step'
                    % REVISIT: Cast axes to double for dispatching
                    hResponsePlot = stepplot(double(this.Handles.Axes),tf(1,[1 1],'TimeUnit',this.TimeUnit),tf(1,[1 1],'TimeUnit',this.TimeUnit));
                    hResponsePlot.AxesGrid.XUnits = this.TimeUnit;
                    response = hResponsePlot.Responses(this.TunedAxesIndex);
                    response.DataFcn = {@LocalTimeDataFcn response this};
                case 'bode'
                    % REVISIT: Cast axes to double for dispatching
                    hResponsePlot = bodeplot(double(this.Handles.Axes),tf(1,[1 1],'TimeUnit',this.TimeUnit),tf(1,[1 1],'TimeUnit',this.TimeUnit));
                    hResponsePlot.AxesGrid.XUnits = this.FreqUnit;
            end
            hResponsePlot.AxesGrid.Title = '';
            hResponsePlot.AxesGrid.Grid = 'on';
            % customize responses
            hh = get(hResponsePlot,'Responses');
            set(hh(this.BaseAxesIndex),'Name',pidtool.utPIDgetStrings(this.Platform,'plotpanel_baseresp'));
            set(hh(this.TunedAxesIndex),'Name',pidtool.utPIDgetStrings(this.Platform,'plotpanel_tunedresp'));
            hh(this.BaseAxesIndex).setstyle('Color',this.BaseAxesColor);
            hh(this.TunedAxesIndex).setstyle('Color',this.TunedAxesColor);
            mRoot = hResponsePlot.AxesGrid.findMenu('waveforms');
            set(mRoot,'visible','off');
            this.Handles.hResponsePlot = hResponsePlot;
        end
        
        function setDisplayLegend(this)
            if this.ShownLegend
                PlotType = this.PlotTypeContents{this.Handles.PlotTypeComboBoxCOMPONENT.getSelectedIndex+1};
                PlotSystem = this.PlotSystemContents{this.Handles.PlotSystemComboBoxCOMPONENT.getSelectedIndex+1};
                switch PlotType
                    case 'step'
                        switch PlotSystem
                            case {'r2y' 'r2u' 'plant'}
                                legend(this.Handles.Axes,'location','southeast')
                            case {'id2y' 'od2y'}
                                legend(this.Handles.Axes,'location','northeast')
                            otherwise
                                legend(this.Handles.Axes,'location','northwest')
                        end
                    case 'bode'
                        legend(this.Handles.Axes,'location','southwest')
                end
            else
                legend(this.Handles.Axes,'hide')
            end
            hgroup = handle(this.Handles.hResponsePlot.Responses(this.BaseAxesIndex).Group);
            if this.Handles.ShowBaseCheckBoxCOMPONENT.isSelected
                for ct = 1:length(hgroup)
                    hgroup(ct).Annotation.LegendInformation.IconDisplayStyle = 'on';
                end
            else
                for ct = 1:length(hgroup)
                    hgroup(ct).Annotation.LegendInformation.IconDisplayStyle = 'off';
                end
            end
        end
        
        % set visibility of table
        function showTable(this)
            if this.ShownTable
                set(this.Handles.TablePanelCONTAINER,'Visible','on')
                this.Handles.MoreButtonCOMPONENT.setIcon(com.mathworks.common.icons.CommonIcon.BACKWARD.getIcon);
                this.Handles.MoreTextCOMPONENT.setText(pidtool.utPIDgetStrings('cst','plotpanel_more_hide'));
            else
                set(this.Handles.TablePanelCONTAINER,'Visible','off')
                this.Handles.MoreButtonCOMPONENT.setIcon(com.mathworks.common.icons.CommonIcon.FORWARD.getIcon);
                this.Handles.MoreTextCOMPONENT.setText(pidtool.utPIDgetStrings('cst','plotpanel_more_show'));
            end
        end
        
    end
    methods
        function val = get.Form(this)
            val = this.TunedController.Form;
        end
        function val = get.ViewDOF(this)
            if ~isempty(this.BaseController)
                baseDOF = this.BaseController.DOF;
            else
                baseDOF = 0;
            end
            tunedDOF = this.TunedController.DOF;
            
            val = max(tunedDOF,this.Handles.ShowBaseCheckBoxCOMPONENT.isSelected*baseDOF);
        end
    end
    
end

%%%%%%%%%%%%%%%%%%%%
% LocalTimeDataFcn %
%%%%%%%%%%%%%%%%%%%%
function LocalTimeDataFcn(response,this)
% Data function for time plots
timeresp(response.DataSrc,'step',response,this.FinalTime);
end
