classdef (CaseInsensitiveProperties=true) fvtool < sigtools.sigfig & sigio.dyproputil & matlab.mixin.Copyable
  
  %   Copyright 2015-2023 The MathWorks, Inc.
  
  %sigtools.fvtool class
  %   sigtools.fvtool extends sigtools.sigfig.
  %
  %    sigtools.fvtool properties:
  %       Alphamap - Property is of type 'mxArray'
  %       CloseRequestFcn - Property is of type 'mxArray'
  %       Color - Property is of type 'mxArray'
  %       Colormap - Property is of type 'mxArray'
  %       CurrentAxes - Property is of type 'mxArray'
  %       CurrentCharacter - Property is of type 'mxArray'
  %       CurrentObject - Property is of type 'mxArray'
  %       CurrentPoint - Property is of type 'mxArray'
  %       DockControls - Property is of type 'on/off'
  %       FileName - Property is of type 'mxArray'
  %       IntegerHandle - Property is of type 'on/off
  %       InvertHardcopy - Property is of type 'on/off'
  %       KeyPressFcn - Property is of type 'mxArray'
  %       KeyReleaseFcn - Property is of type 'mxArray'
  %       MenuBar - Property is of type 'mxArray'
  %       Name - Property is of type 'mxArray'
  %       NextPlot - Property is of type 'mxArray'
  %       NumberTitle - Property is of type 'on/off'
  %       PaperUnits - Property is of type 'mxArray'
  %       PaperOrientation - Property is of type 'mxArray'
  %       PaperPosition - Property is of type 'mxArray'
  %       PaperPositionMode - Property is of type 'mxArray'
  %       PaperSize - Property is of type 'mxArray'
  %       PaperType - Property is of type 'mxArray'
  %       Pointer - Property is of type 'mxArray'
  %       PointerShapeCData - Property is of type 'mxArray'
  %       PointerShapeHotSpot - Property is of type 'mxArray'
  %       Position - Property is of type 'mxArray'
  %       Renderer - Property is of type 'mxArray'
  %       RendererMode - Property is of type 'mxArray'
  %       Resize - Property is of type 'on/off'
  %       ResizeFcn - Property is of type 'mxArray'
  %       SelectionType - Property is of type 'mxArray'
  %       ToolBar - Property is of type 'mxArray'
  %       Type - Property is of type 'mxArray'
  %       Units - Property is of type 'mxArray'
  %       WindowButtonDownFcn - Property is of type 'mxArray'
  %       WindowButtonMotionFcn - Property is of type 'mxArray'
  %       WindowButtonUpFcn - Property is of type 'mxArray'
  %       WindowKeyPressFcn - Property is of type 'mxArray'
  %       WindowKeyReleaseFcn - Property is of type 'mxArray'
  %       WindowScrollWheelFcn - Property is of type 'mxArray'
  %       WindowStyle - Property is of type 'mxArray'
  %       BeingDeleted - Property is of type 'mxArray'
  %       ButtonDownFcn - Property is of type 'mxArray'
  %       Children - Property is of type 'mxArray'
  %       Clipping - Property is of type 'on/off'
  %       CreateFcn - Property is of type 'mxArray'
  %       DeleteFcn - Property is of type 'mxArray'
  %       BusyAction - Property is of type 'mxArray'
  %       HandleVisibility - Property is of type 'mxArray'
  %       HitTest - Property is of type 'on/off'
  %       Interruptible - Property is of type 'on/off'
  %       Parent - Property is of type 'mxArray'
  %       Selected - Property is of type 'on/off'
  %       SelectionHighlight - Property is of type 'on/off'
  %       Tag - Property is of type 'mxArray'
  %       UIContextMenu - Property is of type 'mxArray'
  %       UserData - Property is of type 'mxArray'
  %       Visible - Property is of type 'on/off'
  %       AnalysisToolbar - Property is of type 'on/off'
  %       FigureToolbar - Property is of type 'on/off'
  %       Grid - Property is of type 'on/off'
  %       Legend - Property is of type 'on/off'
  %       DesignMask - Property is of type 'on/off'
  %       Fs - Property is of type 'double_vector user-defined'
  %       SOSViewSettings - Property is of type 'dspopts.sosview'
  %
  %    sigtools.fvtool methods:
  %       addfilter - Add a filter to FVTool
  %       currentanalysis_listener - Listener to the current analysis
  %       deletefilter - Delete a filter from FVTool
  %       findall - find all objects.
  %       findfilters -   Find the filters in the input.
  %       findobj -   Find objects matching specified conditions.
  %       fvtool_cbs - FVTool Callbacks.
  %       legend -   Add a legend to FVTool.
  %       loadobj -  Load this object.
  %       notification_listener - Listener to the Notification event
  %       parsedigitalfilterinputs - Parse digitalFilter object inputs
  %       parsesysobjinputs - Parse System object inputs
  %       saveobj -  Save this object.
  %       setfilter - Set the filter to FVTool
  %       settitle - Set the title of the Filter Visualization Tool
  %       visible_listener - Listener to the visible property of FVTool
  %       zoom -   Zoom
  
  properties
    %GRID Property is of type 'on/off'
    Grid = 'off';
    %LEGEND Property is of type 'on/off'
    Legend = 'off';
  end
    
  properties (AbortSet, SetObservable, GetObservable)
    %DESIGNMASK Property is of type 'on/off'
    DesignMask = 'off';
    %SOSVIEWSETTINGS Property is of type 'dspopts.sosview'
    SOSViewSettings = [];
  end
  
  properties (AbortSet, SetObservable, GetObservable, Hidden)
    %FILTERS Property is of type 'MATLAB array' (hidden)
    Filters = [];
    %FSEDITABLE Property is of type 'on/off' (hidden)
    FsEditable
    %HOSTNAME Property is of type 'String' (hidden)
    HostName = '';
    %SPECTRALMASK Property is of type 'dspdata.maskline' (hidden)
    SpectralMask = [];
  end
  
  properties (AbortSet, SetObservable, GetObservable, Hidden)
    %ANALYSISTOOLBAR Property is of type 'on/off'
    AnalysisToolbar = 'on';
    %FIGURETOOLBAR Property is of type 'on/off'
    FigureToolbar = 'on';
  end
  
  properties (Access=protected, AbortSet, SetObservable, GetObservable)
    %LISTENERS Property is of type 'handle.listener vector'
    Listeners = [];
    %ANALYSISPARAMETERPROPS Property is of type 'schema.prop vector'
    AnalysisParameterProps = [];
    %PARAMETERLISTENERS Property is of type 'handle.listener vector'
    ParameterListeners = [];
  end
  
  properties (SetObservable, GetObservable)
    %FS Property is of type 'double_vector user-defined'
    Fs = [];
  end
  
  properties (Access=private)
    IsOpenWithLinkAndReplace = false; % Set to true when fvtool supposed to have both link button and replace checkbox
    IsOpenWithReplaceOnly = false; % Set to true when fvtool supposed to have replace checkbox only
    IsOpenWithLinkOnly = false; % Set to true when fvtool supposed to have link button only
    Document
  end
  
  properties (Access=protected)
    DynamicPropNames = {'Analysis', 'OverlayedAnalysis'};
  end
  
  events
    NewPlot
  end  % events
  
  methods  % constructor block
    function this = fvtool(varargin)
      %FVTOOL The constructor for the FVTool object.
      
      import matlab.ui.internal.FigureDocument;
      
      [varargin{:}] = convertStringsToChars(varargin{:});
      [varargin, analysisStr, optstruct, pvpairs] = parse_inputs(this, varargin{:});
      
      % Disable the NextPlot warning
      [wstr, wid] = lastwarn('');
      w = warning('off', 'MATLAB:HandleGraphics:SupersededProperty:NextPlotNew');
      
      hFVT = siggui.fvtool;
      % Set flag to distinguish fvtool
      set(hFVT, 'IsFVTool', true);
      
      addcomponent(this, hFVT);
      addplugins(hFVT);
      % Create AppContainer if it already doesn't exist
      if isempty(sigtools.fvtool.setGetInstance())
          
          import matlab.ui.internal.FigureDocumentGroup; %#ok<*SIMPT>
          
          % Create AppContainer
          appOptions.Tag = "FilterVisualizationTool";
          appOptions.Title = getString(message('signal:sigtools:sigtools:FilterVisualizationTool'));
          appContainer = matlab.ui.container.internal.AppContainer(appOptions);
          appContainer.Busy = true;
          [xOffset, yOffset, width, height] = signal.internal.utilities.getDefaultWindowSize();
          appContainer.WindowBounds = [xOffset yOffset width height];
          addlistener(appContainer, 'StateChanged', @(src,evt) sigtools.fvtool.handleAppStateChange(src,evt));
          sigtools.fvtool.setGetInstance(appContainer);

          % Add a document group
          group = FigureDocumentGroup();
          group.Tag = "fvToolFigureDocumentGroup";
          group.Title = getString(message('signal:sigtools:sigtools:FilterVisualizationTool'));
          appContainer.add(group);

          % Render toolstrip
          render_toolstrip(this);
      end      

      % Add a figure-based document
      appContainer = sigtools.fvtool.setGetInstance();
      appContainer.Busy = true;

      figOptions.DocumentGroupTag = appContainer.getDocumentGroup("fvToolFigureDocumentGroup").Tag;
      document = FigureDocument(figOptions);
      this.Document = document;

      % Set listener on document being destroyed.
      addlistener(document, 'ObjectBeingDestroyed', @(src, evt)closeEmptyFVTTool);
      % Store respective 'siggui.fvtool' in document to use for global toolstrip events
      document.UserData.hFVT = hFVT;
      appContainer.add(document);

      % Save Figure handle and set properties
      this.sigfig_construct(document.Figure);

      lastwarn(wstr, wid);
      warning(w);
      
      [filters, hasfs] = findfilters(this, varargin{:});
      set(hFVT, 'Filters', filters);
            
      dp1 = this.addprop('Analysis');
      dp1.SetMethod = @(s,e)setanalysis(this,e,'Analysis');
      dp1.GetMethod = @(this)getanalysis(this,'','Analysis');
      
      dp2 = this.addprop('OverlayedAnalysis');
      dp2.SetMethod = @(s,e)setanalysis(this,e,'OverlayedAnalysis');
      dp2.GetMethod = @(s,e)getanalysis(this,'','OverlayedAnalysis');
      
      addplugins(this);

      % Set the Current Analysis
      set(hFVT, 'Analysis', analysisStr);
      currentanalysis_listener(this);
      
      if hasfs && isprop(this, 'NormalizedFrequency')
        this.NormalizedFrequency = 'off';
      end
      
      % Set all the inputs before rendering so that we only update once.
      
      if ~isempty(optstruct)
        hFVT = getcomponent(this, 'siggui.fvtool');
        hPrm = get(hFVT, 'Parameters');
        struct2param(hPrm, optstruct);
      end
      
      % We have to attach the listeners first so that setting the analysis will
      % update the dynamic properties.
      attachlisteners(this);
      
      % Make sure the properties are up to date.
      currentanalysis_listener(this);
      
      % Check the filter for FDESIGN to draw the masks.  Do this before setting
      % the P/V Pairs in case the P/V pairs disable the mask.
      Hd = get(this, 'Filters');
      if isfdtbxinstalled
        
        if isa(Hd{1}, 'dfilt.basefilter')
          hfdfirst = privgetfdesign(Hd{1});
          hfmfirst = getfmethod(Hd{1});
        else
          hfdfirst = [];
          hfmfirst = [];
        end
        
        if isempty(hfdfirst) || isempty(hfmfirst)
            hasmask = false;
        else
            hasmask = true;
        end
        
        % Loop over the rest of the filters, but break early if any of them
        % dont have a mask or it does not match the first.
        indx = 2;
        while indx <= length(Hd) && hasmask
          
          if isa(Hd{indx}, 'dfilt.basefilter')
            hfd = privgetfdesign(Hd{indx});
            hfm = getfmethod(Hd{indx});
          else
            hfd = [];
            hfm = [];
          end
          
          % If there is no fdesign or fmethod, or if they do not match the
          % first set, then we cannot draw a mask.
          if isempty(hfd) || isempty(hfm) || ...
              ~isequivalent(hfdfirst, hfd) || ...
              isconstrained(hfm) ~= isconstrained(hfmfirst)
            hasmask = false;
          end
          indx = indx + 1;
        end
        
        if hasmask
          pvpairs = [{'DesignMask', 'On'}, pvpairs];
        end
      end
      
      fdesignOptions = getFVToolOptions(this, filters);
      pvpairs = [fdesignOptions, pvpairs];
      
      % Set the param value pairs one at a time.
      for indx = 1:length(pvpairs)/2                
        try
          % Allow partial completion and case insensitive dynamic property
          % names.
          prop = validatestring(pvpairs{2*indx-1},this.DynamicPropNames);                          
          this.(prop) = pvpairs{2*indx};
        catch
          % If property not found on the dynamic property list, then let
          % set error out.
          this.(pvpairs{2*indx-1}) = pvpairs{2*indx};
        end
      end
      
      % Make sure the properties are up to date.
      currentanalysis_listener(this);

      render(hFVT, this.FigureHandle);
      setunits(hFVT,'Normalized');
      set(hFVT, 'Visible', 'On');
      
      % Install Listeners
      this.Tag = 'filtervisualizationtool';
            
      set(hFVT.CurrentAnalysis, 'Filters', get(hFVT, 'Filters'));

      % If fvtool is called from Filter Designer add IsOpenWithLinkAndReplace
      % flag to the corresponding document so Filter Designer buttons can
      % be enabled/disabled on document selection change.
      document.UserData.IsOpenWithLinkAndReplace = this.IsOpenWithLinkAndReplace;
      document.UserData.IsOpenWithReplaceOnly = this.IsOpenWithReplaceOnly;
      document.UserData.IsOpenWithLinkOnly = this.IsOpenWithLinkOnly;
      if this.IsOpenWithLinkAndReplace
          % This is when fvtool is already open and a new document is
          % opened to have both Link And Replace Button.
          % Render Analysis tab with Link and Replace buttons.
          document.UserData.LinkButtonSelectedState = true;
          document.UserData.ReplaceCheckBoxSelectedState = true;
      elseif this.IsOpenWithReplaceOnly
          % This is when fvtool is already open and a new document is
          % opened to have Link And Replace Button.
          % Render Analysis tab Link.
          document.UserData.LinkButtonSelectedState = false;
          document.UserData.ReplaceCheckBoxSelectedState = true;
      elseif this.IsOpenWithLinkOnly
          % This is when fvtool is already open and a new document is
          % opened to have Link And Replace Button.
          % Render Analysis tab Replace.
          document.UserData.LinkButtonSelectedState = true;
          document.UserData.ReplaceCheckBoxSelectedState = false;
      end

      % Make AppContainer Visible
      if ~appContainer.Visible
          appContainer.Visible = true;
      end
      appContainer.bringToFront;
      
      % Wait for document to be opened completely
      waitfor(document, "Opened", true);      
        
      % Render Axes toolbar buttons
      render_axes_toolbar_items(this,document);
      
      % Resize the listbox according to axes size
      document.Figure.AutoResizeChildren = 'off';
      document.Figure.SizeChangedFcn = @(~,~)fix_listbox_position(this,hFVT);
     
      % Set document title
      document.UserData.FigureIndex = sigtools.fvtool.getBestFigureIndex();
      settitle(this);     

      % Add listener for document property change
      addlistener(document, 'PropertyChanged', @(src, evt) sigtools.fvtool.handleDocumentPropertyChange(src, evt, this));

      % Force toolstrip updates
      evt = struct;
      evt.PropertyName = "Selected";
      sigtools.fvtool.handleDocumentPropertyChange(document, evt, this);
      appContainer.Busy = false;
    end  % fvtool    
    
    %-------------------------------------------------------------------    
  end  % constructor block

  methods

    function close(obj,varargin)

        % Capture number of open figures
        appContainer = sigtools.fvtool.setGetInstance();
        NumberOfDocs = numel(appContainer.getDocuments());

        % Wait until container is closed to return control
        if ~isempty(appContainer) && NumberOfDocs < 2
            close(appContainer);
            waitfor(appContainer,'State',matlab.ui.container.internal.appcontainer.AppState.TERMINATED);
        else
            % Close figure
            close@sigtools.sigfig(obj,varargin{:});
        end
    end

    function set.AnalysisToolbar(obj,value)
      % DataType = 'on/off'
      value = validatestring(value,{'on','off'},'','AnalysisToolbar');
      obj.AnalysisToolbar = value;
    end
    
    function set.FigureToolbar(obj,value)
      % DataType = 'on/off'
      value = validatestring(value,{'on','off'},'','FigureToolbar');
      obj.FigureToolbar = value;
    end
    
    function value = get.Filters(obj)
      value = getfilters(obj,obj.Filters);
    end
    function set.Filters(obj,value)
      obj.Filters = setfilters(obj,value);
    end
    
    function value = get.Grid(obj)
      value = getfcn(obj,obj.Grid,'Grid');
    end
    function set.Grid(obj,value)
      % DataType = 'on/off'
      if ~isa(value, 'matlab.lang.OnOffSwitchState')
        value = validatestring(value,{'on','off'},'','Grid');
      end
      obj.Grid = setfcn(obj,value,'Grid');
      fix_view_section_buttons(obj)
    end
    
    function value = get.Legend(obj)
      value = getfcn(obj,obj.Legend,'Legend');
    end
    function set.Legend(obj,value)
      % DataType = 'on/off'
      if ~isa(value, 'matlab.lang.OnOffSwitchState')
        value = validatestring(value,{'on','off'},'','Legend');
      end
      obj.Legend = setfcn(obj,value,'Legend');
    end
    
    function value = get.DesignMask(obj)
      value = getfcn(obj,obj.DesignMask,'DisplayMask');
    end
    function set.DesignMask(obj,value)
      % DataType = 'on/off'
      value = validatestring(value,{'on','off'},'','DesignMask');
      obj.DesignMask = setfcn(obj,value,'DisplayMask');
      fix_view_section_buttons(obj)
    end
    
    function value = get.Fs(obj)
      value = getfs(obj,obj.Fs);
    end
    function set.Fs(obj,value)
      % User-defined DataType = 'double_vector user-defined'
      obj.Fs = setfs(obj,value);
    end
    
    function value = get.FsEditable(obj)
      value = getfcn(obj,obj.FsEditable,'fseditable');
    end
    function set.FsEditable(obj,value)
      % DataType = 'on/off'
      value = validatestring(value,{'on','off'},'','FsEditable');
      obj.FsEditable = setfcn(obj,value,'fseditable');
    end
    
    function set.HostName(obj,value)
      % DataType = 'String'
      % no cell string checks yet'
      obj.HostName = value;
    end
    
    function value = get.SOSViewSettings(obj)
      value = get_sosview(obj,obj.SOSViewSettings);
    end
    function set.SOSViewSettings(obj,value)
      % DataType = 'dspopts.sosview'
      validateattributes(value,{'dspopts.sosview'}, {'scalar'},'','SOSViewSettings')
      obj.SOSViewSettings = set_sosview(obj,value);
    end
    
    function value = get.SpectralMask(obj)
      value = get_maskline(obj,obj.SpectralMask);
    end
    function set.SpectralMask(obj,value)
      % DataType = 'dspdata.maskline'
      validateattributes(value,{'dspdata.maskline'}, {'scalar'},'','SpectralMask')
      obj.SpectralMask = set_maskline(obj,value);
    end
    
    function set.Listeners(obj,value)
      % DataType = 'handle.listener vector'
      validateattributes(value,{'event.listener'}, {'vector'},'','Listeners')
      obj.Listeners = value;
    end
    
    function set.AnalysisParameterProps(obj,value)
      % DataType = 'schema.prop vector'
      if ~isempty(value)
        validateattributes(value,{'meta.DynamicProperty'}, {'vector'},'','AnalysisParameterProps')
      end
      
      obj.AnalysisParameterProps = value;
    end
    
    function set.ParameterListeners(obj,value)
      % DataType = 'handle.listener vector'
      if ~isempty(value)
        validateattributes(value,{'event.proplistener'}, {'vector'},'','ParameterListeners')
      end
      obj.ParameterListeners = value;
    end
    
    
    
  end   % set and get functions
  
  methods (Hidden)
    function dynpropsetfunction(this,newVal,hprm)
      
      % Set the dependent property
      setvalue(hprm, newVal);
      
    end
    
    function val = dynpropgetfunction(this,hprm)
      % This function hold the property name and the value of the dynamic
      % property being set.
      val = get(hprm, 'Value');
    end
  
  end
  
 
  methods  %% public methods
    function addfilter(this, varargin)
      %ADDFILTER Add a filter to FVTool
      
      narginchk(2,inf);
      if nargin > 1
          [varargin{:}] = convertStringsToChars(varargin{:});
      end
      
      % Check for System object inputs and the corresponding arithmetic
      % specification. Convert to DFILT objects according to the parsed
      % arithmetic.
      varargin = parsesysobjinputs(this, varargin{:});
      
      % Check for digitalFilter object inputs. Conver to DFILT objects.
      varargin = parsedigitalfilterinputs(this,varargin{:});
      
      filters = findfilters(this, varargin{:});
      
      if isempty(filters(1).Fs)
        maxfs = max(get(this, 'Fs'));
        if isnan(maxfs)
          maxfs = 1;
        end
        for indx = 1:length(filters)
          set(filters(indx), 'Fs', maxfs);
        end
      end
      
      hFVT = getcomponent(this, 'siggui.fvtool');
      
      hFVT.addfilter(filters);

      fix_view_section_buttons(this);
      
    end
    
    function currentanalysis_listener(this, ~)
      %CURRENTANALYSIS_LISTENER Listener to the current analysis
      
      hFVT = getcomponent(this, 'siggui.fvtool');
      
      h  = get(this, 'Handles');
      ca = get(hFVT, 'CurrentAnalysis');
      
      if ~isempty(h)
        if isa(ca, 'sigresp.analysisaxis')
          enab = 'on';
        else
          enab = 'off';
        end
        
        set([h.toolbar.legend h.toolbar.grid], 'Enable', enab);
      end
      
      oldp = get(this, 'AnalysisParameterProps');
      delete(oldp);
      oldl = get(this, 'ParameterListeners');
      delete(oldl);
      
      l = [];
      if isempty(ca)
        p = [];
      else
        
        hPrm = getparameter(ca, '-all');
        if isempty(hPrm)
          p = [];
        end
        
        for indx = 1:length(hPrm)
          hindx = hPrm(indx);
          
          % Get the parameter name
          name = rmspaces(get(hindx, 'Name'));
          
          % Create a property based on the parameter object.
          try
            p(indx) = this.addprop(name);
            p(indx).GetObservable = true;
            p(indx).SetObservable = true;
            p(indx).Dependent = true;
                        
          catch %#ok<*CTCH>
            name = [name '2'];
            p(indx) = this.addprop(name);
            p(indx).GetObservable = true;
            p(indx).SetObservable = true;
            p(indx).Dependent = true;
          end
          
          if ~any(strcmp(this.DynamicPropNames,name))
            this.DynamicPropNames{end+1} = name;
          end
          
          this.(name) = get(hindx, 'Value');
          
          % The following function is used to store the dynamic property name
          % and its new value information inside the object.
          p(indx).SetMethod = @(s,e)dynpropsetfunction(s,e,hindx);          
          p(indx).GetMethod = @(s,e)dynpropgetfunction(s,hindx);
          
        end
        
      end
      
      this.AnalysisParameterProps = p;
      this.ParameterListeners = l;
      
    end
    
    function deletefilter(hObj, varargin)
      %DELETEFILTER Delete a filter from FVTool
      
      hFVT = getcomponent(hObj, 'siggui.fvtool');
      
      hFVT.deletefilter(varargin{:});

      fix_view_section_buttons(hObj);
      
    end    
    
    function h = findall(this, varargin)
      %FINDALL find all objects.
      
      h = findall(double(this), varargin{:});
      
    end
    
    
    function [filters, hasfs] = findfilters(this, varargin)
      %FINDFILTERS   Find the filters in the input.
      
      Fs = []; %#ok<*PROPLC> 
      for indx = 1:length(varargin)
        if ischar(varargin{indx}) && strcmpi(varargin{indx}, 'fs')
          Fs = varargin{indx+1};
          varargin(indx:end) = [];
          break;
        end
      end
      
      if ispref('SignalProcessingToolbox', 'DefaultFs')
        defaultFs = getpref('SignalProcessingToolbox', 'DefaultFs');
      else
        defaultFs = 1;
      end
      
      hfvt = getcomponent(this, 'siggui.fvtool');
      
      [filters, index] = hfvt.findfilters(varargin{:});
      for indx = index.object
        
        % Copy the filter, but make sure we keep the mask info.
        mi = [];
        if isprop(filters(indx).Filter, 'MaskInfo')
          mi = get(filters(indx).Filter, 'MaskInfo');
        end
        filters(indx).Filter = copy(filters(indx).Filter);
        if ~isempty(mi)
            if isa(filters(indx).Filter,'dfilt.basefilter')
                addprop(filters(indx).Filter,'MaskInfo');
            else
                schema.prop(filters(indx).Filter, 'MaskInfo', 'mxArray');
            end
          set(filters(indx).Filter, 'MaskInfo', mi);
        end
      end
      
      % Set the filter's Sampling Frequency to [] and we will fix it later
      % depending on the inputs.
      set(filters(setdiff(1:length(filters), index.objectwfs)), 'Fs', []);
      
      if isempty(Fs)
        
        % When we don't have any filters we won't have an old Fs.  The default
        % is 1, so we use that instead.
        if isempty(this.Filters) || isempty(this.Filters{1})
          oldfs = defaultFs;
        else
          oldfs = get(this, 'Fs');
        end
        
        maxindx = min(length(filters), length(oldfs));
        
        for indx = 1:maxindx
          
          % If the old Fs is not a nan and the current filter doesn't already have
          % an Fs we want to use the old Fs.
          if ~isnan(oldfs(indx)) && isempty(get(filters(indx),'Fs'))
            set(filters(indx), 'Fs', oldfs(indx));
          end
        end
        mfs = max(oldfs);
        if isnan(mfs)
          mfs = defaultFs;
        end
        for indx = maxindx+1:length(filters)
          if isempty(get(filters(indx), 'Fs'))
            set(filters(indx), 'Fs', mfs);
          end
        end
        
      else
        if length(Fs) == 1
          set(filters, 'Fs', Fs);
        else
          if length(filters) ~= length(Fs)
            error(message('signal:sigtools:fvtool:findfilters:lengthMismatch'));
          end
          for indx = 1:length(Fs)
            set(filters(indx), 'Fs', Fs(indx));
          end
        end
      end
      
      hasfs = false;
      empty = [];
      for indx = 1:length(filters)
        hspecs = privgetfdesign(filters(indx).Filter);
        if ~isempty(hspecs) && ~hspecs.NormalizedFrequency
          set(filters(indx), 'Fs', hspecs.Fs);
          hasfs = true;
        elseif isprop(filters(indx).Filter,'SystemObjParams') && isfield(filters(indx).Filter.SystemObjParams,'SampleRate')
          set(filters(indx), 'Fs', filters(indx).Filter.SystemObjParams.SampleRate);
          hasfs = true;
        elseif filters(indx).Fs ~= defaultFs
          hasfs = true;
        else
          empty = [empty indx];
        end
      end
      
      if ~isempty(empty)
        hasfs = false;
        set(filters, 'Fs', defaultFs);
      end
      
      % [EOF]
      
      
    end
    
    
    function h = findobj(this, varargin)
      %FINDOBJ   Find objects matching specified conditions.
      
      h = findobj(double(this), varargin{:});
      
    end
    
    
    function fcns = fvtool_cbs(hFVT) %#ok<MANU> 
      %FVTOOL_CBS FVTool Callbacks.
      
      fcns.new_cb = @new_cb;
      fcns.legend_cb = @legend_cb;
      fcns.grid_cb = @grid_cb;
      fcns.full_view_cb = @full_view_cb;
      fcns.specification_mask_cb  = @specification_mask_cb;
      fcns.polyphase_view_cb = @polyphase_view_cb;
      fcns.reference_filters_cb = @reference_filters_cb;
      fcns.bandzoom_cb = @bandzoom_cb;
      fcns.cb_sos_view_settings_dialog = @cb_sos_view_settings_dialog;
      fcns.cb_user_defined_spectral_mask_dialog = @cb_user_defined_spectral_mask_dialog;
      fcns.cb_analysis_parameters_dialog = @cb_analysis_parameters_dialog;
      fcns.cb_sampling_frequency_dialog = @cb_sampling_frequency_dialog;

    end
    
    
    function legend(hObj, varargin)
      %LEGEND   Add a legend to FVTool.
      %   LEGEND(H, STR1, STR2, etc.) Add a legend to the FVTool associated with
      %   H.  STR1 will be associated with the first filter, STR2 will be
      %   associated with the second filter, etc.
      %
      %   LEGEND(H, ..., 'Location', LOC) Add a legend to FVTool in the location
      %   LOC.  See LEGEND for more information.  LOC is 'Best' by default.
      %
      %   LEGEND(H, 'Location', LOC) Changes the location of the legend without
      %   changing the strings in the legend.
      
      hFVT = getcomponent(hObj, 'siggui.fvtool');
      
      legend(hFVT, varargin{:});        
      fix_view_section_buttons(hObj)
    end
    
    
    function notification_listener(hObj, eventData)
      %NOTIFICATION_LISTENER Listener to the Notification event
      
      notify(hObj, 'Notification', eventData);
      
      NTypes = {'ErrorOccurred','WarningOccurred','StatusChanged','FileDirty'};
      NType  = get(eventData, 'NotificationType');
      
      hFVT = getcomponent(hObj, 'siggui.fvtool');
      
      % Switch on the Notification type. Note that warning and error are methods
      % of fvtool and not the warning and error functions.
      switch NType
        case NTypes{1} % 'ErrorOccurred'
          str = getString(message('signal:sigtools:fvtool:notification_listener:FVToolError'));
          error(hFVT, str, eventData.Data.ErrorString);
        case NTypes{2} % 'WarningOccurred'
          str = getString(message('signal:sigtools:fvtool:notification_listener:FVToolWarning'));
          warning(hFVT, str, eventData.Data.WarningString,eventData.Data.WarningID);
        case NTypes{3} % 'StatusChanged'
          % NO OP.  FVTool has no way of doing this
        case NTypes{4} % File Dirty
          % NO OP.  FVTool has no sessions.
        otherwise
          str1 = getString(message('signal:sigtools:fvtool:notification_listener:FVToolError'));
          str2 = getString(message('signal:sigtools:fvtool:notification_listener:UnhandledNotification',NType,class(eventData.Source)));
          error(hFVT, str1, str2)
      end
      
    end
    
    function cellOut = parsedigitalfilterinputs(~, varargin)
      %PARSEDIGITALFILTERINPUTS Parse digitalFilter object inputs
      
      cellOut = varargin;
      
      % Find digitalFilter object inputs
      for idx = 1:length(cellOut)
        if isa(cellOut{idx},'digitalFilter')
          cellOut{idx} = todfilt(cellOut{idx});
        end
      end
      
    end
        
    function cellOut = parsesysobjinputs(~, varargin)
      %PARSESYSOBJINPUTS Parse System object inputs
      
      sysObjCell = {};
      sysObjCellIndx = [];
      charInputCell = {};
      charInputCellIndx = [];
      
      % Find System object inputs
      for idx = 1:length(varargin)
        if isa(varargin{idx},'dsp.internal.FilterAnalysis')
          sysObjCell = [sysObjCell varargin(idx)]; %#ok<*AGROW>
          sysObjCellIndx = [sysObjCellIndx idx];
        end
        if ischar(varargin{idx})
          charInputCell = [charInputCell varargin(idx)];
          charInputCellIndx = [charInputCellIndx idx];
        end
      end
      
      arith = [];
      % See if an arithmetic input has been passed to the addfilter method, cache
      % the arithmetic and remove the arithmetic inputs from the input varargin
      % cell
      arithIdx = find(strcmpi(charInputCell,'arithmetic'));
      if ~isempty(arithIdx)
        if (length(charInputCell) >= arithIdx+1 &&...
            ~any(strcmpi({'double','single','fixed'},charInputCell{arithIdx+1}))) ...
            || length(charInputCell) < arithIdx+1
          error(message('signal:sigtools:fvtool:fvtool:InvalidArithmeticInput'))
        end
        arith = charInputCell{arithIdx+1};
        varargin([charInputCellIndx(arithIdx) charInputCellIndx(arithIdx+1)]) = [];
        if isempty(sysObjCell)
          warning(message('signal:sigtools:fvtool:fvtool:IrrelevantArithInput'))
        end
      end
      
      % Throw 'same as input' warnings only once
      lastwarn('')
      wId2 = 'dsp:dsp:private:FilterSystemObjectBase:DefaultCoeffDataTypeFIR';
      wId3 = 'dsp:dsp:private:FilterSystemObjectBase:DefaultCoeffDataTypeSOS';
      wId4 = 'dsp:dsp:private:FilterSystemObjectBase:DefaultCoeffDataTypeLockedFIR';
      wId5 = 'dsp:dsp:private:FilterSystemObjectBase:DefaultCoeffDataTypeLockedSOS';
      wState2 =  warning('QUERY', wId2);
      wState3 =  warning('QUERY', wId3);
      wState4 =  warning('QUERY', wId4);
      wState5 =  warning('QUERY', wId5);
      c1 = onCleanup(@()restoreWarningState(wState2,wState3,wState4,wState5));
      
      % Convert input System object filters to DFILT objects. Throw warnings only
      % once
      
      for k = 1:length(sysObjCell)
        indx = sysObjCellIndx(k);
        varargin = parseObjectLevelOptions(sysObjCell{k},varargin);
        varargin{indx} = todfilt(sysObjCell{k},arith);
        [wmsg, wid] = lastwarn; %#ok<ASGLU>
        if strcmpi(wid,wId2)
          warning('off',wId2);
        end
        if strcmpi(wid,wId3)
          warning('off',wId3);
        end
        if strcmpi(wid,wId4)
          warning('off',wId4);
        end
        if strcmpi(wid,wId5)
          warning('off',wId5);
        end
      end
      
      cellOut = varargin;     
    end
        
    function s = saveobj(~)
      %SAVEOBJ  Save this object.
      
      warning(message('signal:fvtool:NotSerializable'));
      
      % Something must be returned to avoid the default object load.
      s.EmptyField = [];      
    end
    
    function setfilter(this, varargin)
      %SETFILTER Set the filter to FVTool
      
      % FINDFILTERS is now done in SETFCN.
      % filters = findfilters(this, varargin{:});
      
      % Check for System object inputs and the corresponding arithmetic
      % specification. Convert to DFILT objects according to the parsed
      % arithmetic.
      varargin = parsesysobjinputs(this, varargin{:});
      
      % Check for digitalFilter object inputs. Conver to DFILT objects.
      varargin = parsedigitalfilterinputs(this,varargin{:});
      
      this.Filters = varargin;     

      fix_view_section_buttons(this);
    end
        
    function settitle(this, document)
      %SETTITLE Set the title of the Filter Visualization Tool           
      if nargin == 2
          selectedDocument = document;
      else
          selectedDocument = sigtools.fvtool.getSelectedDocument();          
      end
      
      if sigtools.fvtool.isValidDocumenthFVT(selectedDocument)         
          userData = selectedDocument.UserData;
          hFVT = userData.hFVT;
          str  = get(hFVT.CurrentAnalysis, 'Name');

          % Set the figure title.
          hn = get(this, 'HostName');
          if ~isempty(hn)
              str = sprintf('%s (%s)', str, hn);
          end

          if ~strcmpi(get(this, 'WindowStyle'), 'docked')
              str = sprintf([getString(message('signal:sigtools:sigtools:FilterVisualizationTool')) ' - %s'], str);
          end
          if isfield(userData,'FigureIndex')
              str = "Figure " + num2str(userData.FigureIndex) + ": " + string(str);
          end
          set(selectedDocument,'Title',str);
      end
    end
    
    function visible_listener(hFVT, eventData)
      %VISIBLE_LISTENER Listener to the visible property of FVTool
      
      visState = get(hFVT, 'Visible');
      
      if strcmpi(visState, 'on')
        fvtool_visible_listener(hFVT, eventData);
      end
      
      hFig = get(hFVT,'FigureHandle');
      set(hFig,'Visible', hFVT.Visible);
      
      if strcmpi(visState, 'off')
        fvtool_visible_listener(hFVT, eventData);
      end
      
    end
    
    
    function zoom(this, varargin)
      %ZOOM   Zoom
      
      narginchk(2,3);
      
      hFVT = getcomponent(this, 'siggui.fvtool');
      
      zoom(hFVT, varargin{:});
      
    end
    
  end  %% public methods
  
  methods (Access = private, Static)
      
      function handleAppStateChange(src,evt)
          % Clears instance when App terminated
          if src.State == matlab.ui.container.internal.appcontainer.AppState.TERMINATED
              if mislocked('sigtools.fvtool.setGetInstance')
                  munlock('sigtools.fvtool.setGetInstance');
              end
              sigtools.fvtool.setGetInstance([]);
          end
      end
      
      function handleDocumentPropertyChange(selectedDocument, evt, this)        
          if evt.PropertyName == "Selected" && selectedDocument.Selected && ...
                  sigtools.fvtool.isValidDocumenthFVT(selectedDocument) &&...
                  sigtools.fvtool.setGetInstance().State ~= matlab.ui.container.internal.appcontainer.AppState.TERMINATED
              hFVT = selectedDocument.UserData.hFVT;
              analysis = hFVT.Analysis;
              overlayedAnalysis = hFVT.OverlayedAnalysis;

              appContainer = sigtools.fvtool.setGetInstance();
              documentGroupUserData = appContainer.getDocumentGroup("fvToolFigureDocumentGroup").UserData;
              
              % --------- Filter Design Section and Analysis Tab-----------
              fix_filter_designer_buttons(this, documentGroupUserData, selectedDocument);
              
              % ------------------------ Galleries ------------------------
              % Analysis Gallery
              documentGroupUserData.AnalysisGallery.(analysis).Value = true;
              % Overlay Analysis Gallery
              overlayAnalysisGallery = documentGroupUserData.OverlayAnalysisGallery;
              if isempty(overlayedAnalysis)
                  % Select none if no Overlay Analysis is active for
                  % document
                  overlayAnalysisGallery.('none').Value = true;
              else
                  overlayAnalysisGallery.(overlayedAnalysis).Value = true;
              end
              % Enable/Disable elements in Analysis Gallery
              fix_analysis(this, hFVT);
              % Enable/Disable elements in Overlay Analysis Gallery
              fix_overlay_analysis(this, hFVT);
              
              % -------------------- View Section ---------------------
              fix_view_section_buttons(this, hFVT)
              % -------------------- Send To Figure Section ---------------------
              % Fix Send to Figure Button
              fix_send_to_figure_button(this, hFVT);

              % -------------------- Axes Toolbar Buttons ---------------------
              % Passband and Stopband Buttons
              fix_bandzoom_buttons(this,selectedDocument);
          end
      end

      function document = getSelectedDocument()
          % Get selected document from app container
          document = [];
          appContainer = sigtools.fvtool.setGetInstance();
          if ~isempty(appContainer.SelectedChild)
              document = appContainer.getDocument(appContainer.SelectedChild.documentGroupTag, appContainer.SelectedChild.tag);
          end
      end
  end
  
  methods (Hidden) %% possibly private or hidden
      
    function cleanup(this)
      %CLEANUP
      
      hFVT = getcomponent(this, 'siggui.fvtool');
      
      % Delete FVTool and the figure object
      delete(hFVT);
      
      % This was for g279483, but we have decided that we want to leave the
      % MDI open because the user may have setup the MDI and removing it might be
      % annoying.
      hFVTs = findall(0, 'tag', 'filtervisualizationtool'); %#ok<NASGU> 
      
      %       if isempty(setdiff(hFVTs, this.double))
      %         com.mathworks.mlservices.MatlabDesktopServices.getDesktop.closeGroup(getString(message('signal:sigtools:sigtools:FilterVisualizationTool')));
      %       end
      
    end
  
    function flag = isNeedToUpdateToolstrip(this)
        flag = false;
        selectedDocument = sigtools.fvtool.getSelectedDocument();
        if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
            return;
        end

        if ~isfield(selectedDocument.UserData,'FigureIndex') || ~isfield(this.Document.UserData,'FigureIndex')
            return;
        end

        flag = (selectedDocument.UserData.FigureIndex == this.Document.UserData.FigureIndex);
    end

  end  %% possibly private or hidden
  
  
  methods (Static, Hidden) %% static methods
      
    function result = setGetInstance(value)
        
    % persistence of a AppContainer instance
    mlock;
    persistent FVToolInstance;
    if nargin
        FVToolInstance = value;
    end
    result = FVToolInstance;

    end

    function flag = isValidDocumenthFVT(document)
        flag = ~isempty(document) && isvalid(document.UserData.hFVT);
    end

    function docIdx = getBestFigureIndex()
        % Loop through all documents and find smallest available index to
        % append to the figure title

        appContainer = sigtools.fvtool.setGetInstance();
        docs = appContainer.getDocuments('fvToolFigureDocumentGroup');
        idxVect = [];
        for kk = 1:numel(docs)
            if isfield(docs{kk}.UserData,'FigureIndex')
                idxVect = [idxVect; docs{kk}.UserData.FigureIndex];
            end
        end
        if isempty(idxVect)
            docIdx = 1;
        else
            idxVect = sort(idxVect);
            v1 = 1:idxVect(end);
            availableIndices = setdiff(v1,idxVect);
            if isempty(availableIndices)
                docIdx = idxVect(end) + 1;
            else
                docIdx = availableIndices(1);
            end
        end
    end

    function this = loadobj(~)
      %LOADOBJ  Load this object.
      
      this = [];
      
    end
    
  end  %% static methods
  
end  % classdef

function sosview = set_sosview(this, sosview)

hFVT = getcomponent(this, 'siggui.fvtool');

set(hFVT, 'SOSViewOpts', copy(sosview));

setup_sosview_listener(this, sosview);
end  % set_sosview



% -----------------------------------------------------------------
function sosview = get_sosview(this, sosview) %#ok<*INUSD>

hFVT = getcomponent(this, 'siggui.fvtool');

sosview = get(hFVT, 'SOSViewOpts');
if isempty(sosview)
  sosview = dspopts.sosview;
  set(hFVT, 'SOSViewOpts', sosview);
else
  sosview = copy(sosview);
end

setup_sosview_listener(this, sosview);
end  % get_sosview


% -------------------------------------------------------------------------
function setup_sosview_listener(this, h)

l(1) = event.proplistener(h, h.findprop('View'),                'PostSet', @(s,e)sosview_listener(this,e));
l(2) = event.proplistener(h, h.findprop('UserDefinedSections'), 'PostSet', @(s,e)sosview_listener(this,e));
l(3) = event.proplistener(h, h.findprop('SecondaryScaling'),    'PostSet', @(s,e)sosview_listener(this,e));

setappdata(this, 'sosview_listener', l);
end  % setup_sosview_listener


% -----------------------------------------------------------------
function sosview_listener(this, eventData)

sosview = eventData.AffectedObject;
hFVT    = getcomponent(this, 'siggui.fvtool');

set(hFVT, 'SOSViewOpts', copy(sosview));
end  % sosview_listener


% -----------------------------------------------------------------
function h = set_maskline(this, h)

hFVT = getcomponent(this, 'siggui.fvtool');

set(hFVT, 'UserDefinedMask', copy(h));

setup_maskline_listener(this, h);
end  % set_maskline


% -----------------------------------------------------------------
function h = get_maskline(this, h)

hFVT = getcomponent(this, 'siggui.fvtool');

h = get(hFVT, 'UserDefinedMask');
if isempty(h)
  h = dspdata.maskline;
  set(hFVT, 'UserDefinedMask', h);
else
  h = copyTheObj(h);
end

setup_maskline_listener(this, h);
end  % get_maskline


% -------------------------------------------------------------------------
function setup_maskline_listener(this, h)

l(1) = event.proplistener(h, h.findprop('EnableMask'),      'PostSet', @(s,e)maskline_listener(this,e));
l(2) = event.proplistener(h, h.findprop('MagnitudeUnits'),  'PostSet', @(s,e)maskline_listener(this,e));
l(3) = event.proplistener(h, h.findprop('FrequencyVector'), 'PostSet', @(s,e)maskline_listener(this,e));
l(4) = event.proplistener(h, h.findprop('MagnitudeVector'), 'PostSet', @(s,e)maskline_listener(this,e));

% set(l, 'CallbackTarget', this);
setappdata(this, 'maskline_listener', l);
end  % setup_maskline_listener


% -----------------------------------------------------------------
function maskline_listener(this, eventData)

maskline = eventData.AffectedObject;
hFVT     = getcomponent(this, 'siggui.fvtool');

objA = copyTheObj(maskline);

set(hFVT,'UserDefinedMask',objA)

ca = get(hFVT, 'CurrentAnalysis');

if ~isempty(ca) && isprop(ca, 'UserDefinedMask')
  show_listener(ca)
end

sendfiltrespwarnings(hFVT);

end  % maskline_listener


% -----------------------------------------------------------------
function fs = setfs(this, fs)

hFVT = getcomponent(this, 'siggui.fvtool');

filt = get(hFVT, 'Filters');

oldfs = get(this, 'Fs');

if length(fs) == 1
  if isnan(fs)
    set(filt, 'Fs', 1);
  else
    set(filt, 'Fs', fs);
  end
else
  if length(fs) ~= length(filt)
    error(message('signal:sigtools:fvtool:schema:lengthMismatch'));
  end
  for indx = 1:length(filt)
    if isnan(fs(indx))
      set(filt(indx), 'Fs', 1);
    else
      set(filt(indx), 'Fs', fs(indx));
    end
  end
end

if isprop(this, 'NormalizedFrequency')
  this.NormalizedFrequency = 'Off';
end

if ~isequal(oldfs, fs) && isrendered(hFVT), draw(hFVT.CurrentAnalysis); end
end  % setfs


% fs = [];

% -----------------------------------------------------------------
function fs = getfs(this, fs)

hFVT = getcomponent(this, 'siggui.fvtool');

filt = get(hFVT, 'Filters');

for indx = 1:length(filt)
  lfs = get(filt(indx), 'Fs');
  if isempty(lfs)
    fs(indx) = NaN;
  else
    fs(indx) = lfs;
  end
end
end  % getfs


% -----------------------------------------------------------------
function out = setfilters(this, out)

if ~iscell(out)
  out = {out};
end

hFVT = getcomponent(this, 'siggui.fvtool');

% Check for System object inputs and convert to DFILT objects.
out = parsesysobjinputs(this, out{:});

% Check for digitalFilter object inputs and convert to DFILT objects.
out = parsedigitalfilterinputs(this,out{:});

out = this.findfilters(out{:});

hFVT.setfilter(out);
end  % setfilters


% -----------------------------------------------------------------
function out = getfilters(this, out)

hFVT = getcomponent(this, 'siggui.fvtool');

out  = get(hFVT.Filters, 'Filter');

if ~iscell(out), out = {out}; end
end  % getfilters


% -----------------------------------------------------------------
function out = setfcn(this, out, prop)

hFVT = getcomponent(this, 'siggui.fvtool');

hFVT.(prop) = out;
end  % setfcn


% -----------------------------------------------------------------
function out = getfcn(this, out, prop) %#ok<*INUSL>

hFVT = getcomponent(this, 'siggui.fvtool');

out = get(hFVT, prop);
end  % getfcn

% -----------------------------------------------------------------
function attachlisteners(this)

hFVT  = getcomponent(this, 'siggui.fvtool');

hfig = this.FigureHandle;
addlistener(hfig, hfig.findprop('WindowStyle'), 'PostSet', @(h, ev) onWindowStyleChange(this));
addlistener(hfig, 'ObjectChildAdded', @(h, ev) onChildAdded(ev));

l(1) = event.listener(hFVT, 'NewPlot', @(s,e)lclnewplot_listener(this,e));
l(2) = event.proplistener(this, this.findprop('HostName'),        'PostSet', @(s,e)lclhostname_listener(this,e));
l(3) = event.proplistener(hFVT, hFVT.findprop('Grid'),            'PostSet', @(s,e)lclonoff_listener(this,e));
l(4) = event.proplistener(hFVT, hFVT.findprop('Legend'),          'PostSet', @(s,e)lclonoff_listener(this,e));
l(5) = event.proplistener(hFVT, hFVT.findprop('CurrentAnalysis'), 'PostSet', @(s,e) currentanalysis_listener(this,s));
l(6) = event.listener(hFVT, 'NewParameters', @(s,e)currentanalysis_listener(this,e));

this.Listeners = l;
end  % attachlisteners

%-------------------------------------------------------------------
function render_toolstrip(this)

import matlab.ui.internal.toolstrip.TabGroup;

appContainer = sigtools.fvtool.setGetInstance();

% Add TabGroup
tabGroup = TabGroup();
tabGroup.Tag = "toolstripTabGroup";

% Render Analysis Tab
render_analysis_tab(this, tabGroup);

% Add tab Group to AppContainer
appContainer.add(tabGroup);

% Add QAB Help Button
button = render_QABHelp_button(this);
appContainer.add(button);
end

%-------------------------------------------------------------------
function render_analysis_tab(this, tabGroup)

import matlab.ui.internal.toolstrip.Tab;

% Add Analysis Tab
tab = Tab(getString(message('signal:sigtools:fvtool:toolstrip:analysisTabTitle')));
tab.Tag = "analysisTab";

% Render File Section
tab = render_file_section(this, tab);

% Render View Section
tab = render_view_section(this, tab);

% Render Analysis Section
tab = render_analysis_section(this, tab);

% Render Send to Figure Section
tab = render_send_to_figure_section(this, tab);

tab = render_filter_design_section(this, tab);

tabGroup.add(tab);
end

%-------------------------------------------------------------------
function tab = render_file_section(this, tab)

import matlab.ui.internal.toolstrip.Icon;
import matlab.ui.internal.toolstrip.Button;

% Add File Section
section = tab.addSection(getString(message('signal:sigtools:fvtool:toolstrip:fileSectionTitle')));
section.Tag = "fileSection";
column = section.addColumn();

% Add New Button
button = Button(getString(message('signal:sigtools:fvtool:toolstrip:newButton')), Icon.NEW_24);
button.Tag = "newButton";
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:newButtonDesc'));
button.ButtonPushedFcn = {fvtool_cbs(this).new_cb};
column.add(button);

end

%-------------------------------------------------------------------
function tab = render_view_section(this, tab)

import matlab.ui.internal.toolstrip.Icon;
import matlab.ui.internal.toolstrip.Button;
import matlab.ui.internal.toolstrip.ToggleButton;
import matlab.ui.internal.toolstrip.Label;

appContainer = sigtools.fvtool.setGetInstance();
documentGroup = appContainer.getDocumentGroup("fvToolFigureDocumentGroup");

% Add View Section
section = tab.addSection(getString(message('signal:sigtools:fvtool:toolstrip:viewSectionTitle')));
section.Tag = "viewSection";

% Add Column
column = section.addColumn();

% Add Legend Button
image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "legend_16.png");
icon = Icon(image);
button = ToggleButton(icon);
button.Tag = "legendButton";
button.Text = getString(message('signal:sigtools:fvtool:toolstrip:legendButton'));
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:legendButtonDesc'));
button.ValueChangedFcn = {fvtool_cbs(this).legend_cb};
documentGroup.UserData.LegendButton = button;
column.add(button);

% Add Grid Button
image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "grid_16.png");
icon = Icon(image);
button = ToggleButton(icon);
button.Tag = "gridButton";
button.Text = getString(message('signal:sigtools:fvtool:toolstrip:gridButton'));
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:gridButtonDesc'));
button.ValueChangedFcn = {fvtool_cbs(this).grid_cb};
documentGroup.UserData.GridButton = button;
column.add(button);

% Add Empty Control
column.addEmptyControl();

% Add Column
column = section.addColumn();

% Add Specification Mask Button
image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "specificationMask_16.png");
button = ToggleButton(Icon(image));
button.Tag = "specificationMaskButton";
button.Text = getString(message('signal:sigtools:fvtool:toolstrip:specificationMaskButton'));
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:specificationMaskButtonDesc'));
button.ValueChangedFcn = {fvtool_cbs(this).specification_mask_cb};
documentGroup.UserData.SpecificationMaskButton = button;
column.add(button);

% Add Reference Filters View Button
image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "referenceFilter_16.png");
button = ToggleButton(Icon(image));
button.Tag = "referenceFiltersButton";
button.Text = getString(message('signal:sigtools:fvtool:toolstrip:referenceFiltersButton'));
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:referenceFiltersButtonDesc'));
button.ValueChangedFcn = {fvtool_cbs(this).reference_filters_cb};
documentGroup.UserData.ReferenceFiltersButton = button;
column.add(button);

% Add Polyphase View Button
image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "polyphaseView_16.png");
button = ToggleButton(Icon(image));
button.Tag = "polyphaseViewButton";
button.Text = getString(message('signal:sigtools:fvtool:toolstrip:polyphaseViewButton'));
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:polyphaseViewButtonDesc'));
button.ValueChangedFcn = {fvtool_cbs(this).polyphase_view_cb};
documentGroup.UserData.PolyphaseViewButton = button;
column.add(button);

% Add Column
column = section.addColumn();

% Add SOS View Settings Button
image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "sosViewSettings_16.png");
button = Button(Icon(image));
button.Tag = "sosViewSettings";
button.Text = getString(message('signal:sigtools:fvtool:toolstrip:sosViewSettings'));
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:sosViewSettingsDesc'));
button.ButtonPushedFcn = {fvtool_cbs(this).cb_sos_view_settings_dialog};
column.add(button);

% Add User-defined Spectral Mask Button
image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "userDefinedSpectralMask_16.png");
button = Button(Icon(image));
button.Tag = "userDefinedSpectralMask";
button.Text = getString(message('signal:sigtools:fvtool:toolstrip:userDefinedSpectralMask'));
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:userDefinedSpectralMaskDesc'));
button.ButtonPushedFcn = {fvtool_cbs(this).cb_user_defined_spectral_mask_dialog};
column.add(button);

% Add Empty Control
column.addEmptyControl();

end

%-------------------------------------------------------------------
function tab = render_analysis_section(this, tab)

import matlab.ui.internal.toolstrip.GalleryPopup;
import matlab.ui.internal.toolstrip.GalleryCategory;
import matlab.ui.internal.toolstrip.Gallery;
import matlab.ui.internal.toolstrip.Icon;
import matlab.ui.internal.toolstrip.Button;

% Add Analysis Section
section = tab.addSection(getString(message('signal:sigtools:fvtool:toolstrip:analysisSectionTitle')));
section.Tag = "analysisSection";

% Add Column
column = section.addColumn();

% Add popup
popup = GalleryPopup('GalleryItemTextLineCount', 2, 'ShowSelection', true, 'GalleryItemWidth', 110);
popup.Tag = "GalleryPopup";

% Add Analysis Category
categoryTag = "AnalysisGallery";
category = GalleryCategory(getString(message("signal:sigtools:fvtool:toolstrip:analysisGalleryAnalysisCategory")));
popup.add(category);

% Add items to Analysis Category
render_gallery_items(this, category, categoryTag);

% Add Overlay Analysis Category
categoryTag = "OverlayAnalysisGallery";
category = GalleryCategory(getString(message("signal:sigtools:fvtool:toolstrip:overlayAnalysisGalleryOverlayAnalysisCategory")));
popup.add(category);

% Add items to Overlay Analysis Category
render_gallery_items(this, category, categoryTag);

% Add gallery
gallery = Gallery(popup, 'MaxColumnCount', 3);
gallery.Tag  = categoryTag;
column.add(gallery);

% Add Column
column = section.addColumn();

% Add Analysis Parameters Button
image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "analysisParameters_16.png");
icon = Icon(image);
button = Button(getString(message('signal:sigtools:fvtool:toolstrip:analysisParametersButton')), icon);
button.Tag = "analysisParametersButton";
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:analysisParametersButtonDesc'));
button.ButtonPushedFcn = {fvtool_cbs(this).cb_analysis_parameters_dialog};
column.add(button);

% Add Sampling Frequency Button
image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "samplingFrequency_16.png");
icon = Icon(image);
button = Button(getString(message('signal:sigtools:fvtool:toolstrip:samplingFrequencyButton')), icon);
button.Tag = "samplingFrequencyButton";
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:samplingFrequencyButtonDesc'));
button.ButtonPushedFcn = {fvtool_cbs(this).cb_sampling_frequency_dialog};
column.add(button);

end

%-------------------------------------------------------------------
function tab = render_send_to_figure_section(this, tab)

import matlab.ui.internal.toolstrip.Icon;
import matlab.ui.internal.toolstrip.Button;

appContainer = sigtools.fvtool.setGetInstance();
documentGroup = appContainer.getDocumentGroup("fvToolFigureDocumentGroup");

% Add Send To Figure Section
section = tab.addSection(getString(message('signal:sigtools:fvtool:toolstrip:sendToFigureSectionTitle')));
section.Tag = "sendToFigureSection";

% Add a column
column = section.addColumn();

% Add send to figure Button
button = Button(Icon.EXPORT_24);
button.Tag = "sendToFigureButton";
button.Text = getString(message('signal:sigtools:fvtool:toolstrip:sendToFigureButton'));
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:sendToFigureButtonDesc'));
button.ButtonPushedFcn = @(~, ~)sendToFigure_cb(this);
documentGroup.UserData.SendToFigureButton = button;
column.add(button);

end

%-------------------------------------------------------------------
function tab = render_filter_design_section(this, tab)

import matlab.ui.internal.toolstrip.Icon;
import matlab.ui.internal.toolstrip.ToggleButton;
import matlab.ui.internal.toolstrip.CheckBox;

appContainer = sigtools.fvtool.setGetInstance();
documentGroup = appContainer.getDocumentGroup("fvToolFigureDocumentGroup");

% Add Filter Designer Section
section = tab.addSection(getString(message('signal:sigtools:fvtool:toolstrip:filterDesignerSectionTitle')));
section.Tag = "filterDesignerSection";

% Add a column
column = section.addColumn();

image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "link_16.png");
button = ToggleButton(Icon(image));
button.Tag = "linkButton";
button.Text = getString(message('signal:sigtools:fvtool:toolstrip:filterDesignerLinkButton'));
button.Description = getString(message('signal:sigtools:fvtool:toolstrip:filterDesignerLinkButtonDesc'));
documentGroup.UserData.LinkButton = button;
column.add(button);

% Add Replace CheckBox
checkBox = CheckBox();
checkBox.Tag = "replaceCheckBox";
checkBox.Text = getString(message('signal:sigtools:fvtool:toolstrip:filterDesignerReplaceCheckBox'));
checkBox.Description = getString(message('signal:sigtools:fvtool:toolstrip:filterDesignerReplaceCheckBoxDesc'));
documentGroup.UserData.ReplaceCheckBox = checkBox;
column.add(checkBox);

end

%-------------------------------------------------------------------
function render_gallery_items(this, category, categoryTag)
% This method renders items in both analysis and overlay analysis since
% they have similar items. The categoryTag = "AnalysisGallery" or
% categoryTag = "OverlayAnalysisGallery"

import matlab.ui.internal.toolstrip.Icon;
import matlab.ui.internal.toolstrip.ToggleGalleryItem;
import matlab.ui.internal.toolstrip.ButtonGroup;

% Add items to Gallery
hObj = getcomponent(this, 'siggui.fvtool');
if categoryTag == "AnalysisGallery"
    analysisNames = getallanalyses(hObj);
elseif categoryTag == "OverlayAnalysisGallery"
    analysisNames = getOverlayAnalyses(hObj);
end

buttonGroup = ButtonGroup();

for idx = 1:numel(analysisNames)
    analysisName = analysisNames{idx};
    image = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', analysisName + "_24.png");
    icon = Icon(image);
    item = ToggleGalleryItem(getString(message("signal:sigtools:fvtool:toolstrip:" + analysisName)), icon, buttonGroup);
    category.add(item);
    itemTag = analysisName + "_" + categoryTag;
    item.Tag = itemTag;
    item.ValueChangedFcn = {@cb_analysis, this, itemTag};
    items.(analysisName) = item;
end

if categoryTag == "OverlayAnalysisGallery"
    % Select none by default
    items.('none').Value = true;
end

appContainer = sigtools.fvtool.setGetInstance();
documentGroup = appContainer.getDocumentGroup("fvToolFigureDocumentGroup");
documentGroup.UserData.(categoryTag) = items;

end

%-------------------------------------------------------------------
function render_axes_toolbar_items(this, document, analysis)

% currentAxes = this.CurrentAxes;
hFVT = document.UserData.hFVT;
if ~isprop(hFVT,'Handles')
    return;
end
hAxes = hFVT.Handles.axes;
currentAxes = hAxes(2);

if nargin > 2 && any(strcmpi(analysis, ["info", "coefficients"]))
    % If textarea is on, hide all axes toolbar
    hAxes(2).Toolbar = [];
    hAxes(1).Toolbar = [];
else
    axesToolbar = axtoolbar(currentAxes, {'pan','zoomin','zoomout','restoreview'});

    % Change Tooltip for restoreview
    button = axesToolbar.Children.findobj('Tag','restoreview');
    button.Tooltip = getString(message('signal:sigtools:fvtool:toolstrip:defaultViewButtonDesc'));

    % Add Full View Button
    button = axtoolbarbtn(axesToolbar,'push');
    button.Icon = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "fullView_16.png");
    button.Tooltip = getString(message('signal:sigtools:fvtool:toolstrip:fullViewButtonDesc'));
    button.ButtonPushedFcn = {fvtool_cbs(this).full_view_cb, this};

    % Add Passband Button
    button = axtoolbarbtn(axesToolbar,'push');
    button.Icon = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "zoomInPassBand_16.png");
    button.Tooltip = getString(message('signal:sigtools:fvtool:toolstrip:passbandButtonDesc'));
    button.ButtonPushedFcn = {fvtool_cbs(this).bandzoom_cb, 'pass'};
    document.UserData.PassbandButton = button;

    % Add Stopband Button
    button = axtoolbarbtn(axesToolbar,'push');
    button.Icon = fullfile(matlabroot, 'toolbox','signal','sigtools','private','fvtool_icons', "zoomInStopBand_16.png");
    button.Tooltip = getString(message('signal:sigtools:fvtool:toolstrip:stopbandButtonDesc'));
    button.ButtonPushedFcn = {fvtool_cbs(this).bandzoom_cb, 'stop'};
    document.UserData.StopbandButton = button;
end
end

%-------------------------------------------------------------------------
function button = render_QABHelp_button(this)

import matlab.ui.internal.toolstrip.Icon;
button = matlab.ui.internal.toolstrip.qab.QABPushButton();
button.ButtonPushedFcn = @(~, ~)cb_fvtool_help(this);
button.Icon = Icon.HELP_16;
button.Description = getString(message('signal:sigtools:sigtools:FVToolHelp'));

end

%-------------------------------------------------------------------------
function cb_analysis(hcbo, eventStruct, this, tag)

if eventStruct.EventData.NewValue
    appContainer = sigtools.fvtool.setGetInstance();
    % Show Busy Overlay
    appContainer.Busy = true;
    selectedDocument = sigtools.fvtool.getSelectedDocument();
    if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
        return;
    end
    hFig = selectedDocument.Figure;
    userData = selectedDocument.UserData;
    hFVT = userData.hFVT;

    p = getptr(hFig);
    setptr(hFig, 'watch');
    lastwarn('');

    % Set the current analysis to the tag of the HG object.
    analysisSplit = split(tag, "_");
    analysis = char(analysisSplit(1));
    categoryTag = analysisSplit(2);

    if categoryTag == "AnalysisGallery"
        set(hFVT, 'Analysis', analysis);
        fix_analysis(this, hFVT);
        fix_overlay_analysis(this, hFVT);
    elseif categoryTag == "OverlayAnalysisGallery"
        if analysis == "none"
            analysis = '';
        end
        set(hFVT, 'OverlayedAnalysis',analysis);
    end

    set(hFig, p{:});

    % Fix View Section Buttons
    fix_view_section_buttons(this,hFVT);
    % Fix Send to Figure Button
    fix_send_to_figure_button(this,hFVT);
    % Render axestoolbar again because changing analysis unrenders the axis
    % and renders it again.
    render_axes_toolbar_items(this,selectedDocument,analysis);
    % Fix Bandzoom Buttons
    fix_bandzoom_buttons(this,selectedDocument);
    % Hide Busy Overlay
    appContainer.Busy = false;
end

end

%-------------------------------------------------------------------------
function cb_analysis_parameters_dialog(~,~)

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end
hObj = selectedDocument.UserData.hFVT;
editparameters(hObj);

end

%-------------------------------------------------------------------------
function cb_sampling_frequency_dialog(~,~)

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end
hObj = selectedDocument.UserData.hFVT;
editfs(hObj);

end

%-------------------------------------------------------------------------
function specification_mask_cb(src, evt)

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end
hObj = selectedDocument.UserData.hFVT;
set(hObj, 'DisplayMask', char(matlab.lang.OnOffSwitchState(evt.EventData.NewValue)));

end

%-------------------------------------------------------------------------
function fix_view_section_buttons(this, hFVT)

appContainer = sigtools.fvtool.setGetInstance();
documentGroupUserData = appContainer.getDocumentGroup("fvToolFigureDocumentGroup").UserData;

if nargin < 2   
    selectedDocument = sigtools.fvtool.getSelectedDocument();
    if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
        return;
    end
    hFVT = selectedDocument.UserData.hFVT;
end

% Grid Button
documentGroupUserData.GridButton.Value = strcmpi(hFVT.Grid, "on");
% Legend Button
documentGroupUserData.LegendButton.Value = strcmpi(hFVT.Legend, "on");
% Specification Mask Button
[enabState, checked] = displaymask_listener(hFVT);
documentGroupUserData.SpecificationMaskButton.Value = strcmpi(checked, "on");
documentGroupUserData.SpecificationMaskButton.Enabled = strcmpi(enabState, "on");
% Polyphase View Button
documentGroupUserData.PolyphaseViewButton.Value = strcmpi(hFVT.PolyphaseView, "on");
% Reference Filters Button
documentGroupUserData.ReferenceFiltersButton.Value = strcmpi(hFVT.ShowReference, "on");

end

%-------------------------------------------------------------------------
function fix_send_to_figure_button(this, hFVT)

appContainer = sigtools.fvtool.setGetInstance();
documentGroupUserData = appContainer.getDocumentGroup("fvToolFigureDocumentGroup").UserData;
if nargin < 2
    selectedDocument = sigtools.fvtool.getSelectedDocument();
    if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
        return;
    end        
    hFVT = selectedDocument.UserData.hFVT;
end
documentGroupUserData.SendToFigureButton.Enabled = ~any(strcmpi(hFVT.Analysis, ["info", "coefficients"]));

end

%-------------------------------------------------------------------------
function fix_bandzoom_buttons(this, selectedDocument)

userData = selectedDocument.UserData;
hObj = userData.hFVT;
[passEnab, stopEnab] = updatezoommenus(hObj);
if isfield(userData,"PassbandButton") && isvalid(userData.PassbandButton)
    userData.PassbandButton.Visible = matlab.lang.OnOffSwitchState(passEnab);
end
if isfield(userData,"StopbandButton") && isvalid(userData.StopbandButton)
    userData.StopbandButton.Visible = matlab.lang.OnOffSwitchState(stopEnab);
end

end

%-------------------------------------------------------------------------
function fix_filter_designer_buttons(this, documentGroupUserData, selectedDocument)

import matlab.ui.internal.toolstrip.Icon;

% If linked to filter designer then Link button is always
% enabled. Replace button is only enabled when Link
% button linked to filter designer, Link button is
% selected and Replace button was selected earlier.

docUserData = selectedDocument.UserData;
linkButton = documentGroupUserData.LinkButton;
replaceCheckBox = documentGroupUserData.ReplaceCheckBox;

if docUserData.IsOpenWithLinkAndReplace 
    linkButton.Enabled = true;    
    replaceCheckBox.Enabled = true;

    linkButton.Value = selectedDocument.UserData.LinkButtonSelectedState;
    replaceCheckBox.Value = selectedDocument.UserData.ReplaceCheckBoxSelectedState;

elseif docUserData.IsOpenWithReplaceOnly
    linkButton.Enabled = false;
    replaceCheckBox.Enabled = true;

    linkButton.Value = true;
    replaceCheckBox.Value = selectedDocument.UserData.ReplaceCheckBoxSelectedState;
    
elseif docUserData.IsOpenWithLinkOnly
    linkButton.Enabled = true;
    replaceCheckBox.Enabled = false;

    linkButton.Value = selectedDocument.UserData.LinkButtonSelectedState;
    replaceCheckBox.Value = true;    

else
    linkButton.Enabled = false;
    replaceCheckBox.Enabled = false;
    replaceCheckBox.Value = false;    
end
end

%-------------------------------------------------------------------------
function polyphase_view_cb(src, evt)

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end
hFVT = selectedDocument.UserData.hFVT;
set(hFVT, 'PolyphaseView', char(matlab.lang.OnOffSwitchState(evt.EventData.NewValue)));

end

%-------------------------------------------------------------------------
function reference_filters_cb(src, evt)

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end
hFVT = selectedDocument.UserData.hFVT;
set(hFVT, 'ShowReference', char(matlab.lang.OnOffSwitchState(evt.EventData.NewValue)));

end

%-------------------------------------------------------------------------
function bandzoom_cb(~, ~, band)

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end

hFVT = selectedDocument.UserData.hFVT;
zoom(hFVT, [band 'band']);

end

%-------------------------------------------------------------------------
function cb_sos_view_settings_dialog(~,~)

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end

hFVT = selectedDocument.UserData.hFVT;
sosview(hFVT);

end

%-------------------------------------------------------------------------
function cb_user_defined_spectral_mask_dialog(~,~)

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end

hFVT = selectedDocument.UserData.hFVT;
userdefinedmask(hFVT);

end

%-------------------------------------------------------------------------
function legend_cb(hcbo,eventStruct)
%LEGEND_CB OnCallback for Legend On/Off button.

hFig = gcbf;
p    = getptr(hFig);
setptr(hFig, 'watch');

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end

userData = selectedDocument.UserData;
hFVT = userData.hFVT;

set(hFVT,'Legend',matlab.lang.OnOffSwitchState(eventStruct.EventData.NewValue));

set(hFig, p{:});

end

%-------------------------------------------------------------------------
function grid_cb(hcbo,eventStruct)
%LEGEND_CB OnCallback for Legend On/Off button.

hFig = gcbf;
p    = getptr(hFig);
setptr(hFig, 'watch');

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end

userData = selectedDocument.UserData;
hFVT = userData.hFVT;

set(hFVT,'Grid',matlab.lang.OnOffSwitchState(eventStruct.EventData.NewValue));

set(hFig, p{:});

end

%---------------------------------------------------------------------
function new_cb(hcbo,eventStruct)
% Callback for the "New Session" toolstrip pushbutton.
%
% Inputs:
%   Not being used

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end

userData = selectedDocument.UserData;
hFVT = userData.hFVT;

G = get(hFVT, 'Filters');
currentAnalysis = get(hFVT, 'Analysis');
hPrm = copyparams(get(hFVT, 'Parameters'));
   
fvtool(G, hPrm, currentAnalysis);
end

%---------------------------------------------------------------------
function full_view_cb(hcbo, eventStruct, hObj)
% Callback for the "FullView" toolstrip pushbutton.
%
% Inputs:
%   Not being used

hAxes = findobj(hObj.FigureHandle, 'type', 'axes');
for indx = 1:length(hAxes)
    if ~strcmpi(getappdata(hAxes(indx), 'zoomable'), 'off')
        set(hAxes(indx), 'YLimMode', 'auto', 'XLimMode', 'auto');
    end
end

end

%---------------------------------------------------------------------
function sendToFigure_cb(this)
% Callback for the "Send To Figure" toolstrip pushbutton.

selectedDocument = sigtools.fvtool.getSelectedDocument();
if ~sigtools.fvtool.isValidDocumenthFVT(selectedDocument)
    return;
end

userData = selectedDocument.UserData;

hFVT = userData.hFVT;
str  = get(hFVT.CurrentAnalysis, 'Name');
hAxes = hFVT.Handles.axes;
hFig = figure;
hFig.NumberTitle = 'off';
hFig.Name = str;

% hAxes(2) is the axis for analysis and hAxes(1) is for overlay analysis.
% To make overlay analysis visible, we need to copy overlay analysis axis
% on top of analysis axis.
% If overlay analysis is not on or analysis is not freq, we don't need to
% copy the right axes.
isRightAxesNeedsToBeCopied = hFVT.OverlayedAnalysis ~= "" || hFVT.Analysis == "freq";
for idx = numel(hAxes):-1:numel(hAxes)-isRightAxesNeedsToBeCopied
    copyobj(hAxes(idx),hFig);
end

end
%-------------------------------------------------------------------
function cb_fvtool_help(~,~,~)

helpview('signal', 'fvtool_overview');

end

%-------------------------------------------------------------------
%                       Utility Functions
%-------------------------------------------------------------------
%-------------------------------------------------------------------
function analysis = setanalysis(this, analysis, prop)

analysis = convertStringsToChars(analysis);
hFVT = getcomponent(this, 'siggui.fvtool');
set(hFVT, prop, analysis);

appContainer = sigtools.fvtool.setGetInstance();
documentGroupUserData = appContainer.getDocumentGroup("fvToolFigureDocumentGroup").UserData;

% Only update toolstrip if selected document is the same document as the
% one for which analysis is being modified
% ------------------------ Galleries ------------------------
if isNeedToUpdateToolstrip(this)
    % Analysis Gallery
    if string(lower(prop)) == "analysis"
        documentGroupUserData.AnalysisGallery.(analysis).Value = true;
    end

    % Overlay Analysis Gallery
    overlayedAnalysis = hFVT.OverlayedAnalysis;        
    overlayAnalysisGallery = documentGroupUserData.OverlayAnalysisGallery;
    if isempty(overlayedAnalysis)
        % Select none if no Overlay Analysis is active for
        % document
        overlayAnalysisGallery.('none').Value = true;
    else
        overlayAnalysisGallery.(overlayedAnalysis).Value = true;
    end
    if string(lower(prop)) == "analysis"
        % Enable/Disable elements in Analysis Gallery
        fix_analysis(this, hFVT);
        fix_overlay_analysis(this, hFVT);
    end
    
    % Fix View Section Buttons
    fix_view_section_buttons(this);
    % Fix Send to Figure Button
    fix_send_to_figure_button(this);
end
% Render axestoolbar again because changing analysis unrenders the axis
% and renders it again.
render_axes_toolbar_items(this,this.Document,analysis);
% Fix Bandzoom Buttons
fix_bandzoom_buttons(this,this.Document);
% Update title
settitle(this,this.Document)
end  % setanalysis


%-------------------------------------------------------------------
function analysis = getanalysis(this, analysis, prop) 

hfvt = getcomponent(this, 'siggui.fvtool');
analysis = get(hfvt, prop);
end  % getanalysis


%-------------------------------------------------------------------
%   Listeners
%-------------------------------------------------------------------

% -------------------------------------------------------------------
function onChildAdded(eventData)
%Set all legends invisible.

hSrc = eventData.Child;

if isa(hSrc, 'matlab.graphics.illustration.Legend')
  warning(message('signal:sigtools:fvtool:fvtool:useLegendMethod', 'LEGEND', 'help fvtool'));
end
end  % onChildAdded


%-------------------------------------------------------------------
function lclnewplot_listener(this, eventData)

settitle(this);
notify(this, 'NewPlot');
end  % lclnewplot_listener


%-------------------------------------------------------------------
function onWindowStyleChange(this)

settitle(this);
end  % onWindowStyleChange


%-------------------------------------------------------------------
function lclhostname_listener(this, eventData) 

settitle(this);
end  % lclhostname_listener


%-------------------------------------------------------------------
function lclonoff_listener(this, eventData)

prop = eventData.Source.Name;
h = get(this, 'Handles');

if isempty(h)
  return;
end

propVal = this.(prop);

set(h.toolbar.(lower(prop)), 'State', propVal);
end  % lclonoff_listener


%-------------------------------------------------------------------
function lclanalysistoolbar_listener(this, eventData)

h = get(this, 'Handles');

set(h.analysistoolbar, 'Visible', this.AnalysisToolbar);
set(findobj(h.menu.view, 'tag', 'fvtool_showanalysistoolbar'), ...
  'Checked', this.AnalysisToolbar);
end  % lclanalysistoolbar_listener


%-------------------------------------------------------------------
function lclfiguretoolbar_listener(hFVT, eventData)

h = get(hFVT, 'Handles');

set(h.figuretoolbar, 'Visible', hFVT.FigureToolbar);
set(findobj(h.menu.view, 'tag', 'fvtool_showfiguretoolbar'), ...
  'Checked', hFVT.FigureToolbar);
end  % lclfiguretoolbar_listener


%-------------------------------------------------------------------
function lcltoolbar_cb(hcbo, eventData, hFVT, prop)

at = get(hFVT, prop);

if strcmpi(at, 'off')
    at = 'on';    
else                   
    at = 'off';
end

set(hFVT, prop, at);
end  % lcltoolbar_cb


% -----------------------------------------------------
function options = getFVToolOptions(this, filters)

firstOptions = [];

% Loop over each of the filters and check its FDesign.
for indx = 1:length(filters)
  Hd = get(filters(indx), 'Filter'); %#ok<NASGU>
  hfdesign = getfdesign(filters(indx).Filter);
  
  % If any of the filters do not have a contained FDesign, do not use any
  % options returned.
  if isempty(hfdesign)
    options = {};
    return;
  else
    
    % Get the options from the FDesign.
    options = getfvtoolinputs(hfdesign);
    if isempty(firstOptions)
      firstOptions = options;
    else
      
      % If the options do not match the first
      if ~isequal(firstOptions, options)
        options = {};
        return;
      end
    end
  end
end

% Validate the options, if any fail, do not use any of them.
for indx = 1:2:length(options)
  if ~isprop(this, options{indx})
    options = {};
    return;
  end
end
end  % getFVToolOptions


% -----------------------------------------------------
function [varargin, analysisStr, optstruct, pvpairs] = parse_inputs(this, varargin)
% Find the analysis string.  The rest is passed to the object

analysisStr = 'magnitude';
optstruct   = [];
pvpairs     = {};
msg         = '';

% Check for System object inputs and the corresponding arithmetic
% specification. Convert to DFILT objects according to the parsed
% arithmetic.
varargin = parsesysobjinputs(this,varargin{:});

% Check for digitalFilter object inputs. Conver to DFILT objects.
varargin = parsedigitalfilterinputs(this,varargin{:});

indx = 1;
while indx <= length(varargin) && isempty(pvpairs)
  if ischar(varargin{indx})
    if indx == length(varargin)
      analysisStr = varargin{indx};
    elseif isstruct(varargin{indx+1})
      analysisStr = varargin{indx};
      optstruct = varargin{indx+1};
    else
      pvpairs = varargin(indx:end);
      varargin = varargin(1:indx-1);
    end
  elseif isstruct(varargin{indx})
    optstruct = varargin{indx};
  elseif isnumeric(varargin{indx}) || isa(varargin{indx}, 'qfilt') || ...
      isa(varargin{indx}, 'dfilt.basefilterMCOS') || isa(varargin{indx}, 'dfilt.basefilter') || ...
      isa(varargin{indx}, 'sigdatatypes.parameter') || ...
      isa(varargin{indx}, 'dfilt.dfiltwfs')
    % NO OP.  These are the filters.
  elseif iscell(varargin{indx})
    msg = 'cell arrays';
  else
    msg = class(varargin{indx});
  end
  indx = indx + 1;
  if ~isempty(msg)
    error(message('signal:sigtools:fvtool:fvtool:invalidInputs', msg));
  end
end
end  % parse_inputs



% -------------------------------------------------------------------------
function s = rmspaces(s)

indx = strfind(s, ' ');
s(indx) = [];
openp = strfind(s, '(');
for indx = length(openp):-1:1
  closep = min(strfind(s(openp(indx):end), ')'));
  if isempty(closep)
    s(openp(indx):end) = [];
  else
    s(openp(indx):openp(indx)+closep-1) = [];
  end
end

end

%-------------------------------------------------------------------
function h = copyparams(hold)

for i = 1:length(hold)
  h(i) = sigdatatypes.parameter(hold(i).Name, hold(i).Tag, ...
    hold(i).ValidValues, hold(i).Value);
end

end


%--------------------------------------------------------------------------
function restoreWarningState(wState1,wState2,wState3,wState4)
% Restore nonRelevantProperty warning state
warning(wState1)
if nargin > 1
  warning(wState2)
  warning(wState3)
  warning(wState4)
end

end

%--------------------------------------------------------------------------
function fix_analysis(this, hFVT)
%FIX_ANALYSIS   Fix the analysis category.

% Get the analysis info that contains the check function.
aInfo = get(hFVT, 'AnalysesInfo');
fn = fieldnames(aInfo);
fn = setdiff(fn, 'tworesps'); % Remove tworesps
appContainer = sigtools.fvtool.setGetInstance();
documentGroupUserData = appContainer.getDocumentGroup("fvToolFigureDocumentGroup").UserData;
analysisGallery = documentGroupUserData.AnalysisGallery;

% Loop over each analysis and check that the filters are valid.
for indx = 1:length(fn)
    if ~isempty(aInfo.(fn{indx}).check) && ~feval(aInfo.(fn{indx}).check, hFVT.Filters)
        % Disable the menu and toolbars if the analysis isn't valid.
        isEnabled = false;
        % If we are on an invalid analysis go back to magnitude.
        if strcmpi(hFVT.Analysis, fn{indx})
            set(hFVT, 'Analysis', 'magnitude');
        end
    else
        isEnabled = true;
    end
    analysisGallery.(fn{indx}).Enabled = isEnabled;
end

end

%--------------------------------------------------------------------------
function fix_overlay_analysis(this, hFVT)
%FIX_OVERLAY_ANALYSIS   Fix the overlay analysis category.

ha = get(hFVT, 'CurrentAnalysis');

if isa(ha, 'filtresp.tworesps') && strcmpi(ha.fvtool_tag, 'tworesps')
    ha = ha.Analyses(1);
end

tag  = get(ha, 'fvtool_tag');

tags = getOverlayAnalyses(hFVT);
overlayedAnalysis = hFVT.OverlayedAnalysis;    

freqtags = {'magnitude', 'phase','grpdelay','phasedelay','magestimate','noisepower'};
timetags = {'impulse', 'step'};

if isempty(tag)
    ontags = {};
else
    switch tag
        case freqtags
            ontags = intersect(tags, freqtags);
        case timetags
            ontags = timetags;
        otherwise
            ontags = {};
    end
end

appContainer = sigtools.fvtool.setGetInstance();
documentGroupUserData = appContainer.getDocumentGroup("fvToolFigureDocumentGroup").UserData;
analysisGallery = documentGroupUserData.AnalysisGallery;
overlayAnalysisGallery = documentGroupUserData.OverlayAnalysisGallery;

for indx = 1:length(tags)
    if tags{indx} == "none"
        isEnabled = true;
    elseif any(strcmpi(tags{indx}, ontags))
        isEnabled = analysisGallery.(tags{indx}).Enabled;
    else
        isEnabled = false;
    end
    overlayAnalysisGallery.(tags{indx}).Enabled = isEnabled;
    if ~isEnabled
        % If button is disabled, also unselect it and select none
        overlayAnalysisGallery.(tags{indx}).Value = false;
        if isempty(overlayedAnalysis) || string(overlayedAnalysis) == string(tags{indx})
            overlayAnalysisGallery.('none').Value = true;
        end
    end
end

end

%--------------------------------------------------------------------------
function fix_listbox_position(this, hFVT)
%FIX_LISTBOX_POSITION   Fix the postion of listbox.

% Set List box Position same as axes
h = get(hFVT,'Handles');
defPos = getpixelposition(h.axes(2));
set(h.listbox, 'Position', defPos);

end

%--------------------------------------------------------------------------
function closeEmptyFVTTool(src, evt)

appContainer = sigtools.fvtool.setGetInstance();
if isempty(appContainer.getDocuments())
    close(appContainer);
    waitfor(appContainer,'State',matlab.ui.container.internal.appcontainer.AppState.TERMINATED);
end
end