classdef DialogContent < matlab.mixin.Heterogeneous & handle & dynamicprops
    % Abstract class for constructing objects that represent dialog
    % contents.  DialogContent instances are intended to be used
    % with DialogPresenter objects to visualize the dialog content.
    %
    % Subclasses must implement:
    %   - createContents()
    
    %   Copyright 2010-2014 The MathWorks, Inc.

    % Why subclass from dynamicprops?
    %
    % Dynamic properties may be added to DialogContent for certain
    % DialogBorder subclasses that add serializable properties.  
    % Such properties are rare.
    %
    % These properties MAY be in use, depending upon whether optional
    % services of a DialogBorder are utilized.
    %
    % An example is RollerShade, which is an optional service of at least
    % DBTopBar, that affects visibility of a dialog.  The setting is
    % typically serialized and restored with the dialog content. So this
    % property needs to be available.

    events
        % Thrown when public properties are updated, if onPropertyChange()
        % is first called on a DialogContent instance.
        PropertyUpdate
    end
    
    properties (Access=private)
        IDStream % Integer ID stream generator
    end
    
    properties (SetAccess=private, Hidden)
        % ID is a unique positive integer assigned to DialogContent
        % when create() is called, identifying a unique instance
        % of dialog content.  Prior to the call to create(), ID=0
        % which means uninitialized.
        ID = 0
        
        % Used to prevent adding redundant property listeners
        PropertyUpdateEventsEnabled = false
    end
    
    properties (SetAccess=protected, Hidden)
        % Handle to uipanel that will contain the primary graphical
        % elements of this dialog when it is rendered.
        %
        % This uipanel is allocated/owned by DialogContent.
        ContentPanel
        
        % True if subclass implements buildDialogContextMenu()
        %
        % xxx use introspection to answer this automatically,
        %     and then remove this property.
        CustomContextHandler = false
        
        % Handle to DialogBorder parent.
        % We do not own this allocation.
        DialogBorder
    end
    
    properties (Hidden)
        % True signals execution context is during lazy-loading.
        % During that phase, we are only interested in data associations,
        % such as hierarchical uidialogs and the properties they define.
        % We are not interested in graphical instantiation or execution.
        IsLazyLoadPhase = false
    end
    
    properties (SetAccess=private,Dependent,Hidden)
        % Accessor to more easily get to top-level
        DialogPresenter
    end
    
    properties (SetAccess=protected)
        Name = '' % Name of dialog, may be translated
        Tag  = '' % Tag string, useful to find dialog in program code
    end
    
    properties
        % Application-defined data
        UserData
        Dialog
    end
    
    methods (Static,Sealed, Access=protected)
        function dialogContent = getDefaultScalarElement
            % This method must be overridden for HeterogeneousHandle 
            % classes when the root class is abstract.  It must return a
            % scalar concrete value.  Only root classes are permitted to 
            % implement this method.  For DialogContent, we return a handle
            % to a dialog with no content.
            dialogContent = dialogmgr.DCEmpty;
        end
    end
    
    methods
        function ext = getExtent(dialogContent,ymax)
            % Return extent of dialog content.
            %
            % Returns all-zero extent until initialize() is called.
            %
            % Optional override of ymax is provided, generally for use
            % during dialog animation.
            
            cp = dialogContent.ContentPanel;
            if nargin<2
                ymax = cp.Position(4);
            end
            if isempty(cp)
                ext = zeros(1,4);
            else
                ext = [0 0 cp.Position(3) ymax];
            end
        end
        
        function y = getHeight(dialogContent)
            % Return height of dialog content panel.
            
            cp = dialogContent.ContentPanel;
            if isempty(cp)
                y = 0;
            else
                y = cp.Position(4);
            end
        end
        
        function setWidth(dialogContent,W)
            % Update width of content panel.
            %
            % Width is of the dialogContent panel, lying interior to the
            % dialogBorder and interior to any decorative border it might
            % display.
            dialogContent.ContentPanel.Position(3) = W;
        end
        
        function W = getWidth(dialogContent)
            % Get current width of dialog content in pixels.
            
            % Easiest to ask DialogBorder for this:
            W = getInnerWidth(dialogContent.DialogBorder);
        end
        
        function W = getMinWidth(dialogContent)
            % Minimum render size of DialogContent in pixels.
            %
            % Minimum size is the smallest width and height that the
            % content can be rendered without clipping.
            %
            % For DialogContent superclass, this is just the width.
            % For DCTableForm or other subclasses with layout managers,
            % there may be a difference between current width and minimum
            % width, so this may be overridden in subclasses.
            
            W = getInnerWidth(dialogContent.DialogBorder);
        end
        
        function dp = get.DialogPresenter(dc)
            dp = dc.DialogBorder.DialogPresenter;
        end
        
        function changeDialogContentHeight(dlg) %#ok<MANU>
            % Force dialog layout to respond to a change in height of the
            % content, such as a change in row visibility.
            
            % (Intentionally left blank.)
        end
    end
    
    methods (Sealed)
        function initialize(dialogContent,dialogBorder)
            % Create DialogContent by invoking subclass implementation
            % via template method pattern.
            %
            % contentParent derives from dialogBorder.
            
            % Cache handle for use when DialogContent must resize, etc
            dialogContent.DialogBorder = dialogBorder;
            setContent(dialogBorder,dialogContent);
            contentParent = getDialogContentParent(dialogBorder);
            
            % Protect against repeated calls by checking .ContentPanel
            if isempty(dialogContent.ContentPanel)
                initializeWithParent(dialogContent,contentParent);
                
                % Now we're ready to create the dialog content.
                % This is implemented by subclasses:
                %
                createContent(dialogContent);
            else
                reinitialize(dialogContent,contentParent);
            end
            
            % This is ESSENTIAL for supporting base-class DialogContent
            % dialogs.  The DCTableForm dialogs do not need this.
            %
            % It's not needed during init since it gets called at finalize.
            % AND ... it saves time during initial load.
            %
            changeDialogContentHeight(dialogBorder, ...
                getHeight(dialogContent), dialogContent);
        end
        
        function detach(dialogContent) %#ok<MANU>
            % Separate dialogContent from dialogBorder parent
            %
            % Keep dialogContent panel and widgets intact, since the
            % intent is to attach this dialogContent to a new
            % dialogBorder parent.
            
            % Nothing to do.
        end
        
        function finalize(dialogContent)
            % Tear down dialog content
            
            % Perform any dialogContent-specific finalization tasks
            %finalizeContent(dialogContent);
            
            % Destroy main content uipanel
            delete(dialogContent.ContentPanel);
            dialogContent.ContentPanel = [];
            
            % Clear out ID to indicate uninitialized state
            dialogContent.ID = 0;
        end
    end
    
    methods
        function initializeFinal(dialogContent) %#ok<MANU>
            % Initialization tasks to complete after all dialogs have been
            % registered with DialogPresenter.
            
            % Intended to be overridden by subclasses.
        end
    end
    
    methods (Access=private)
        function initializeWithParent(dialogContent,uiParent)
            % Create DialogContent by invoking subclass implementation
            % via template method pattern.
            %
            % Explicit assignment of contentParent.
            %
            % Position of dialogContent is derived from uiParent position
            %  - [1 1] origin (relative to uiParent)
            %  - (dx,dy) same as uiParent
            
            % Assign a sequential ID for each DialogContent instance
            %  - ID tracks the allocation of .ContentPanel uipanel
            %  - if uipanel is deallocated, ID is cleared
            dialogContent.ID = generate(dialogContent.IDStream);
            
            % Create uipanel owned by DialogContent
            %
            % See setPosition() for position details.
            dialogContent.ContentPanel = uipanel( ...
                'Parent', uiParent, ...
                'BorderType','none', ...
                'Units','pixels', ...
                'Tag','DialogContent_ContentPanel', ...
                'Position',[1 1 uiParent.Position(3:4)]);
        end
        
        function reinitialize(dialogContent,uiParent)
            % Reinitialization call
            % Re-parent widgets to new graphical dialogBorder panel
            
            % Assumes dialogContent.ContentPanel already exists
            panel = dialogContent.ContentPanel;
            cm = panel.UIContextMenu;
            if ~isempty(cm)
                panel.UIContextMenu = [];
                cm.Parent = ancestor(uiParent, 'figure');
            end
            panel.Parent = uiParent;
            if ~isempty(cm)
                panel.UIContextMenu = cm;
            end
            
            % Reflect our color to parent container
            bg = panel.BackgroundColor;
            uiParent.BackgroundColor = bg;
            uiParent.Parent.BackgroundColor = bg; % xxx grandparent too?
            
            % Reflect our dialogContent size up to parent.
            % Keep parent container's origin/offset.
            %
            pos = uiParent.Position;
            pos(3:4) = panel.Position(3:4);
            uiParent.Position = pos;
        end
    end
    
    methods (Abstract,Access=protected)
        % Create widgets specific to the concrete Dialog subclass.
        % Must be overridden in subclass.
        
        createContent(dc)
    end
    
    methods
        function lazyLoaderDataInit(dc) %#ok<MANU>
            % Establish data properties in parent.
            %
            % Some subclasses, such as DCTableForm, need to execute their
            % content-creation method (initTable), so define child
            % uidialogs and the data properties that they contain.
            %
            % Others do nothing if they don't support hierarchy and child
            % dialogs.
        end
    end
    
    methods
        function update(dialogContent)
            % Subclasses may override this method as needed to update
            % controls within dialog.
            %
            % Default is to take no update actions.
            
            % For backward compatibility, a call to updateContent is made
            % for clients that override that obsolete method:
            updateContent(dialogContent);
        end
    end
    
    methods (Access=protected)
        function updateContent(dialogContent) %#ok<MANU>
            % OBSOLETE METHOD for compatibility only.
            % DO NOT OVERRIDE THIS METHOD
            %
            % Override public method update(dlg) in subclass instead.
        end
        
        %{
        function finalizeContent(dialogContent) %#ok<MANU>
            % Tasks to perform before dialog is destroyed.
            % Intended for override in subclass.
            % Default is to take no action.
        end
        %}
        
        %{
        function resizeDialogContent(dialogContent)
            % Resize dialog content widgets, based on change in size of
            % parent panel in the dialogBorder object that contains this
            % dialogContent object.
            
        end
        %}
    end

    % For client app listening to property changes on DialogContent
    %
    methods
        function onPropertyChange(dialogContent,cbFcn)
            % Execute callback function cbFcn after a change is made to
            % properties of a subclass of DialogContent.
            %
            % Properties of interest must have the following attributes:
            %  - GetAccess = 'public'
            %  - SetObservable = true
            %  - Hidden = false
            %  - Constant = false
            %
            % By default, callback cbFcn is invoked with two arguments:
            % eventSource and eventData. Pass an anonymous function handle
            % to remove or replace these two arguments.
            %
            % Ex:
            %    onPropertyChange(dlg, ...
            %          @(h,ev)PropChangeHandler(myObj,ev));
            
            enablePropertyUpdateEvents(dialogContent);
            addlistener(dialogContent,'PropertyUpdate',cbFcn);
        end
    end
    
    methods (Access=private)
        function enablePropertyUpdateEvents(dialogContent)
            % Enable notification of public property post-set changes by
            % adding listeners to each qualified property.  Throws
            % PropertyUpdate event on each such change.
            
            if dialogContent.PropertyUpdateEventsEnabled
                warning(message('Spcuilib:dialogmgr:WarnPropertyUpdateEventsAlreadyEnabled'));
                return
            end
            
            m = metaclass(dialogContent);
            p = m.PropertyList;
            
            % Find properties that have the "basic" attributes.
            % Those are all attribs specified above except SetObservable.
            %
            % If there are no properties with these "basic" attributes,
            % silently return without installing any listeners.
            basic = strcmpi({p.GetAccess},'public') & ~[p.Constant] & ~[p.Hidden];
            if ~any(basic)
                return
            end
            
            % There is at least one property that is a candidate for
            % listeners.
            %
            % See if there are any properties with SetObservable. Failing
            % to set "SetObservable" is a very common mistake for the
            % client, so we throw a warning if NONE of the properties that
            % made it this far have this attribute.
            obs = [p.SetObservable];
            if ~any(obs)
                warning(message('Spcuilib:dialogmgr:WarnNoSetObservableProperties'));
                return % Early return
            end
            
            % Create and attach listeners for each property
            propNames = {p(basic & obs).Name};
            for i = 1:numel(propNames)
                p_i = propNames{i};
                
                % Broadcast a 'PropertyUpdate' event similar to
                % DCTableForm.
                %
                % We could just have the listener call the client callback
                % directly, but the event payload will be different.
                %
                cbFcn = @(~,~)notify(dialogContent,'PropertyUpdate', ...
                    dialogmgr.DCEvent(p_i,'single',dialogContent));
                
                addlistener(dialogContent,p_i,'PostSet',cbFcn);
            end
            
            % Record that we've added these listeners:
            dialogContent.PropertyUpdateEventsEnabled = true;
        end
    end
    
    methods (Sealed)
        % Services that cannot be overridden
        function onPropertyPostSet(dc,propNames,cbFcn)
            % Helper function to set up listeners for dialog property
            % changes.  By default, callback cbFcn is invoked with two
            % arguments for a Property event: eventSource and eventData.
            % Pass an anonymous function handle to remove or replace these
            % two arguments.
            %
            % Ex:
            %    onPropertyPostSet(dlg, {'PropOne','PropTwo'}, ...
            %          @(h,ev)PropChangeHandler(myObj,ev));
            %
            
            % force propnames into a cell
            if ~iscell(propNames)
                propNames = {propNames};
            end
            % Create and attach listeners for each property
            for i = 1:numel(propNames)
                addlistener(dc,propNames{i},'PostSet',cbFcn);
            end
        end
        
        function onPropertyPreSet(dc,propNames,cbFcn)
            % Helper function to set up listeners for dialog property
            % changes.  By default, callback cbFcn is invoked with two
            % arguments for a Property event: eventSource and eventData.
            % Pass an anonymous function handle to remove or replace these
            % two arguments.
            %
            % Ex:
            %    onPropertyPreSet(dlg, {'PropOne','PropTwo'}, ...
            %          @(h,ev)PropChangeHandler(myObj,ev));
            %
            
            % force propnames into a cell
            if ~iscell(propNames)
                propNames = {propNames};
            end
            % Create and attach listeners for each property
            for i = 1:numel(propNames)
                addlistener(dc,propNames{i},'PreSet',cbFcn);
            end
        end
        
        function t = test(varargin)
            % Launch a test application hosting the dialog with Dialog
            % Manager.
            %
            % test(D) hosts one or more dialogs specified in vector D.
            % test(D1,D2,...) hosts multiple dialogs specified in a list.
            %
            % This method must be sealed in order to support an array of
            % heterogeneous dialogs - i.e., a set of dialogs that subclass
            % DialogContent, DCTableForm and DCTableReadout.
            
            t = dialogmgr.DialogTester(varargin{:});
        end
    end
    
    methods
        function h = DialogContent
            % Construct a DialogContent object.
            
            % Initialize integer stream generator that will produce a
            % unique integer ID for each DialogContent object.
            h.IDStream = dialogmgr.IntegerStreamGenerator( ...
                'dialogmgr_DialogContent', 1);
        end
        
        function buildDialogContextMenu(dc,dp) %#ok<INUSD>
            % Create context menu content specific to this dialog
            % Intended for override in subclass.
            % Must remain public so DialogPresenter can invoke this.
            % Default is to take no action.
        end

        function changeFontSize(dlg,dir) %#ok<INUSD>
            % Intended for override in subclass.
            % Default is to take no action.
        end
    end
    
    methods (Hidden)
        function onCloseDialog(~)
          % No op. Let the subclass override this method to implement a
          % close dialog routine.
        end
    end
    
    % Widget creation methods.
    methods
        function h = uicheckbox(dlg,str,varargin)
            % Create checkbox dialog widget.
            % String is required, value defaults to false.
            % Provides message catalog lookup for string.
            
%             if nargin<3
%                 val=0;
%             end
            if nargin<2
                str = '';
            end
            if isa(str,'message') % work with message catalog
                str = getString(str);
            end
            h = uicontrol( ...
                'Parent',dlg.ContentPanel, ...
                'BusyAction','cancel', ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'Style','checkbox', ...
                'Value',0, ...
                'String',str);
            if nargin>3 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end
        
        function h = uiedit(dlg,str,varargin)
            % Create edit dialog widget.
            % String defaults to empty.
            
            if nargin<2
                str = '';
            end
            h = uicontrol( ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'Style','edit', ...
                'String',str);
            if nargin>2 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end
        
        function h = uieditd(dlg,str,varargin)
            % Create an edit uicontrol that renders simple text in its
            % disabled state.  Compare with the uiedit control that renders
            % a disabled edit box in its disabled state.  These two edit
            % controls have very different appearances when disabled.  All
            % other operation is identical.
            
            if nargin<2
                str = '';
            end
            h = dialogmgr.uieditd( ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'String',str);
            if nargin>2 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end

        function h = uieditvc(dlg,str,varargin)
            % Create a complex-valued scalar numeric edit uicontrol.
            %
            % This control also renders simple text in its
            % disabled state.  Compare with the uiedit control that renders
            % a disabled edit box in its disabled state.  These two edit
            % controls have very different appearances when disabled.  All
            % other operation is identical.
            
            if nargin<2
                str = '';
            end
            
            % Gather up all P/V pairs to send at once to uieditvc:
            pv = { ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'String',str};
            
            if nargin>2 && ~isempty(varargin)
                pv = [pv varargin];
            end
            
            h = dialogmgr.uieditvc(pv{:});
        end
        
        function h = uieditv(dlg,str,varargin)
            % Create a real-valued scalar numeric edit uicontrol.
            %
            % This control also renders simple text in its disabled state.
            % Compare with the uiedit control that renders a disabled edit
            % box in its disabled state.  These two edit controls have very
            % different appearances when disabled.  All other operation is
            % identical.
            
            if nargin<2
                str = '';
            end
            
            % Gather up all P/V pairs to send at once to uieditv:
            pv = { ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'String',str};
            
            if nargin>2 && ~isempty(varargin)
                pv = [pv varargin];
            end
            
            h = dialogmgr.uieditv(pv{:});
        end
        
        function h = uiframe(dlg,color,varargin)
            % Create frame dialog widget.
            
            if nargin<2
                color = dlg.ContentPanel.BackgroundColor;
            end
            h = uicontrol( ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',color, ...
                'Style','frame');
            if nargin>2 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end
        
        function h = uihline(dlg,th,color,varargin)
            % Create horizontal line widget.
            
            if nargin<2
                th = 1;
            end
            if nargin<3
                color = 'k'; % black
            end
            h = dialogmgr.uihline( ...
                'Parent',dlg.ContentPanel, ...
                'Thickness',th, ...
                'ForegroundColor',color, ...
                varargin{:});
        end
        
        function h = uilistbox(dlg,strs,val,varargin)
            % Create listbox dialog widget.
            % Cell-string strs is required.
            % Initial value val may be an index or a string.
            % Message catalog translation is supported.
            
            if (~iscellstr(strs) && ~isstring(strs)) || isempty(strs)
                error(message('Spcuilib:dialogmgr:ErrorNeedNonEmptyCellString','uilistbox'));
            end
            if isa(strs{1},'message')
                strs = cellfun(@getString,strs,'UniformOutput',false);
            end
            
            if nargin<3
                val = 1;
            elseif ~isnumeric(val)
                if isa(val,'message')
                    val = getMessage(val);
                elseif ~ischar(val) && ~(isstring(val) && isscalar(val))
                    error(message('Spcuilib:dialogmgr:ErrorInitialValueMustBeIndexOrString','uilistbox'));
                end
                val = find(strcmpi(val,strs));
                if isempty(val)
                    val = 1;
                end
            end
            
            h = uicontrol( ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'Style','listbox', ...
                'String',strs, ...
                'Value',val);
            if nargin>3 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end
        
        function h = uipopup(dlg,strs,val,varargin)
            % Create popupmenu dialog widget.
            % Cell-string strs is required.
            % Initial value val may be an index or a string.
            % Message catalog translation is supported.
            
            if (~iscellstr(strs) && ~isstring(strs)) || isempty(strs)
                error(message('Spcuilib:dialogmgr:ErrorNeedNonEmptyCellString','uipopup'));
            end
            if isa(strs{1},'message')
                strs = cellfun(@getString,strs,'UniformOutput',false);
            end
            
            if nargin<3
                val = 1;
            elseif ~isnumeric(val)
                sval = val;
                if isa(sval,'message')
                    sval = getMessage(sval);
                elseif ~ischar(sval) && ~(isstring(val) && isscalar(val))
                    error(message('Spcuilib:dialogmgr:ErrorInitialValueMustBeIndexOrString', ...
                        'Initial value for uipopup must be a numeric index or a string.'));
                end
                val = find(strcmpi(sval,strs));
                if isempty(val)
                    warning(message('Spcuilib:dialogmgr:WarnInvalidInitialPopupValue', ...
                        sval,dlg.Name));
                    val = 1; % default to first entry
                end
            end
            
            h = uicontrol( ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'Style','popupmenu', ...
                'String',strs, ...
                'Value',val);
            if nargin>3 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end
        
        function h = uipushbutton(dlg,str,varargin)
            % Create pushbutton dialog widget.
            % String is required.
            % Message catalog translation of string is supported.
            
            if isa(str,'message')
                str = getString(str);
            end
            h = uicontrol( ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'Style','pushbutton', ...
                'String',str);
            if nargin>2 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end
        
        function h = uiradiobutton(dlg,str,val,varargin)
            % Create radiobutton dialog widget.
            % String is required.
            % Message catalog translation of string is supported.
            
            if isa(str,'message')
                str = getString(str);
            end
            if nargin<3
                val=0;
            end
            h = uicontrol( ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'Style','radiobutton', ...
                'Value',val, ...
                'String',str);
            if nargin>3 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end
        
        function h = uislider(dlg,range,val,varargin)
            % Create slider dialog widget with continuous slide action.
            
            if nargin<3
                % Default value is min value.
                % Don't do halfway, as we don't know if integers are
                % desired.
                val = range(1);
            end
            
            h = uicontrol( ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'Style','slider', ...
                'Min',range(1), ...
                'Max',range(2), ...
                'Value',val);
            
            % Enable continuous-events when dragging slider bar itself.
            % Otherwise just the arrow-buttons at ends of slider issue
            % continuous events.
            addlistener(h,'ContinuousValueChange', ...
                @(src,~)notify(src,'Action'));
            %addlistener(h,'ContinuousValueChange', ...
            %    @(src,~)uisliderBarDragQuantizeEvent(src));
    
            if nargin>3 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end

        function h = uispintext(dlg,strs,val,varargin)
            % Create a uispintext uicontrol.
            % Cell-string strs is required.
            % Initial value val may be an index or a string.
            % Message catalog translation is supported.

            if (~iscellstr(strs) && ~isstring(strs)) || isempty(strs)
                error(message('Spcuilib:dialogmgr:ErrorNeedNonEmptyCellString','uispintext'));
            end
            if isa(strs{1},'message')
                strs = cellfun(@getString,strs,'UniformOutput',false);
            end
            
            if nargin<3
                val = 1;
            elseif ~isnumeric(val)
                if isa(val,'message')
                    val = getMessage(val);
                elseif ~ischar(val) && ~(isstring(val) && isscalar(val))
                    error(message('Spcuilib:dialogmgr:ErrorInitialValueMustBeIndexOrString','uispintext'));
                end
                val = find(strcmpi(val,strs));
                if isempty(val)
                    val = 1;
                end
            end
            
            h = dialogmgr.uispintext( ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'String',strs, ...
                'Value',val);
            if nargin>2 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end
        
        function h = uitext(dlg,str,varargin)
            % Create text dialog widget.
            % String defaults to empty if not specified.
            % Message catalog translation of string is supported.
            
            if nargin<2
                str = '';
            elseif isa(str,'message')
                str = getString(str);
            end
            
            % Gather up all P/V pairs to send at once to uieditv:
            pv = { ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'Style','text', ...
                'String',str};
            
            if nargin>2 && ~isempty(varargin)
                pv = [pv varargin];
            end
            
            % Remove 'AllowComplex' p/v pair if present
            % No response needed for this property, AND it's not supported
            % in the base HG widget.
            idx = find(strcmpi(pv,'AllowComplex'));
            if ~isempty(idx)
                pv(idx:idx+1) = [];
            end
            
            h = uicontrol(pv{:});
        end
        
        function h = uitogglebutton(dlg,str,val,varargin)
            % Create togglebutton dialog widget.
            % String is an optional value-only argument, and defaults to 0.
            % Message catalog translation of string is supported.

            if nargin<3
                val=0;
            end
            if isa(str,'message')
                str = getString(str);
            end
            h = uicontrol( ...
                'Parent',dlg.ContentPanel, ...
                'BackgroundColor',dlg.ContentPanel.BackgroundColor, ...
                'Style','togglebutton', ...
                'Value',val, ...
                'String',str);
            if nargin>2 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end
        
        function h = uitable(dlg,data,varargin)
            % Create a uitable dialog widget.
            %
            % Data is an optional value-only argument, and defaults to
            % empty.  See uitable and uitabledemo for guidance on setting
            % the parameters.
            %
            % No message catalog translation is provided.
            
            if nargin<2
                data = [];
            end
            
            % Need to use our wrapper-class version, so that Action event
            % is fired.
            h = dialogmgr.uitable( ...
                'Parent',dlg.ContentPanel, ...
                'Data',data);
            if nargin>2 && ~isempty(varargin)
                set(h,varargin{:});
            end
        end
    end
end

function uisliderBarDragQuantizeEvent(h) %#ok<DEFNU>
% Process ContinuousValueChange events from slider.
%
% Only send event if slider is dragged at least minStep distance, and only
% report minStep-quantized value.

% Only notify with value quantized to minStep.
% Only notify if that quantized value changed.

% Quantizes toward "-inf"
qVal = h.Value-rem(h.Value,h.SliderStep(1));
last_qVal = getappdata(h,'LastQVal');
if ~isequal(last_qVal,qVal)
    setappdata(h,'LastQVal',qVal);
    notify(h,'Action'); % ,dialogmgr.DCSliderEvent(qVal));
end

end

% LocalWords:  vis deallocated bordertype MANU dx dy cb dlg ev dialogmgr
