www.gusucode.com > mbcguitools 工具箱 matlab 源码程序 > mbcguitools/+mbcgui/+container/Dialog.m

    classdef Dialog < matlab.mixin.SetGet & matlab.mixin.Copyable
    %mbcgui.container.Dialog class
    %    mbcgui.container.Dialog properties:
    %       Figure - Property is of type 'MATLAB array' (read only)
    %       Buttons - Property is of type 'string' (read only)
    %       DefaultAction - Property is of type 'string'
    %       HelpCode - Property is of type 'string' (read only)
    %       Owner - Property is of type 'MATLAB array' (read only)
    %       ContentBorder - Property is of type 'MATLAB array'
    %       Content - Property is of type 'MATLAB array'
    %       Enabled - Property is of type 'bool'
    %       PersistPosition - Property is of type 'bool'
    %       Name - Property is of type 'string'
    %       Size - Property is of type 'MATLAB array'
    %       Visible - Property is of type 'on/off' (read only)
    %       Resize - Property is of type 'on/off'
    %       Tag - Property is of type 'string'
    %       InfoTitle - Property is of type 'string'
    %       InfoString - Property is of type 'string'
    %       InfoHeight - Property is of type 'double'
    %
    %    mbcgui.container.Dialog methods:
    %       addListeners - Convenience method to add listeners to dialog's store.
    %       closeDialog - Close the dialog.
    %       disableButtons - Disables a dialog's buttons.
    %       enableButtons - Enables a dialog's buttons.
    %       resetPointer - Reset the pointer to previous setting.
    %       setPointer - Set the dialog's pointer
    %       showDialog - Show the dialog
    
    %  Copyright 2007-2015 The MathWorks, Inc.
    
    properties (Hidden,Constant)
        %Tester tester object
        Tester = mbcgui.container.Tester;
    end
    
    properties (AbortSet, SetObservable)
        %DEFAULTACTION Property is of type 'string'
        DefaultAction = '';
        %CONTENTBORDER Property is of type 'MATLAB array'
        ContentBorder = [ 7, 0, 7, 7 ];
        %CONTENT Property is of type 'MATLAB array'
        Content = [];
        %ENABLED Property is of type 'bool'
        Enabled = true;
        %PERSISTPOSITION Property is of type 'bool'
        PersistPosition = false;
        %NAME Property is of type 'string'
        Name = '';
        %SIZE Property is of type 'MATLAB array'
        Size = [ 400, 200 ];
        %RESIZE Property is of type 'on/off'
        Resize
        %TAG Property is of type 'string'
        Tag = '';
        %INFOTITLE Property is of type 'string'
        InfoTitle = '';
        %INFOSTRING Property is of type 'string'
        InfoString = '';
        %INFOHEIGHT Property is of type 'double'
        InfoHeight = 30;
        %ValidationFcn callback to validate dialog before accepting OK
        ValidationFcn
    end
    
    properties (Access=protected, AbortSet)
        %BUTTONLAYOUT Property is of type 'MATLAB array'
        ButtonLayout = [];
        %CONTENTLAYOUT Property is of type 'MATLAB array'
        ContentLayout = [];
        %INFOPANE Property is of type 'MATLAB array'
        InfoPane = [];
        %LISTENERS Property is of type 'MATLAB array'
        Listeners = [];
        %POINTERID Property is of type 'MATLAB array'
        PointerID = [];
        %ACTIONS Property is of type 'MATLAB array'
        Actions = struct();
        %BUTTONHANDLES Property is of type 'MATLAB array'
        ButtonHandles = [];
        %CLOSEACTION Property is of type 'string'
        CloseAction = 'CANCEL';
    end
    
    properties (SetAccess=protected, AbortSet, SetObservable)
        %FIGURE Property is of type 'MATLAB array' (read only)
        Figure = [];
        %BUTTONS Property is of type 'string' (read only)
        Buttons = 'OK_CANCEL_HELP';
        %HELPCODE Property is of type 'string' (read only)
        HelpCode = '';
        %OWNER Property is of type 'MATLAB array' (read only)
        Owner = [];
        %VISIBLE Property is of type 'on/off' (read only)
        Visible
    end
    
    
    events
        DialogShowing
    end  % events
    
    methods  % constructor block
        function obj = Dialog( varargin )
        %mbcgui.container.Dialog Create a dialog with buttons and space for content.
        %
        %   Example:
        %      d = mbcgui.container.Dialog( 'Owner', pFig,...
        %                          'Name', dialogTitle,...
        %                          'Size', [200, 300],...
        %                          'Buttons', 'OK_CANCEL_HELP',...
        %                          'DefaultAction', 'OK',...
        %                          'CloseAction', 'CANCEL' );
        %
        %      d.Content = iCreateLayout( d.Figure );
        %
        %      % Does the waitfor
        %      closeMode = d.showDialog();
        %
        %      % The tag is set to either 'OK' or 'CANCEL'
        %      switch closeMode
        %         case 'OK'
        %             ...
        %         case 'CANCEL'
        %             ...
        %      end
        %
        %   See also mbcgui.container.Dialog.showDialog.
        
        
        % Create the Actions
        obj.Actions.OK     = iMakeAction( obj, @(s,e)obj.closeDialog('OK'),     'OK' );
        obj.Actions.CANCEL = iMakeAction( obj, @(s,e)obj.closeDialog('CANCEL'), 'Cancel' );
        obj.Actions.CLOSE  = iMakeAction( obj, @(s,e)obj.closeDialog('CLOSE'),  'Close' );
        obj.Actions.HELP   = iMakeAction( obj, @(s,e)iShowHelp( obj ),          'Help' );
        
        obj.Figure = xregdialog();
        obj.addListeners( event.listener( obj, 'ObjectBeingDestroyed', @(s,e)iDeleteFigure(obj) ) );
        
        if nargin
            set( obj, varargin{:} );
        end
        
        % add a close dialog property
        mbcgui.hgclassesutil.addprop(obj.Figure, 'CloseMode');
        
        
        InfoProps = [obj.findprop('InfoTitle'), obj.findprop('InfoString'), obj.findprop('InfoHeight')];
        obj.addListeners( event.proplistener(obj, InfoProps, 'PostSet', @iUpdateInfo));
        % Initialise dialog location here, before any contents are created.  This
        % prevents a bug - ActiveX controls not appearing
        obj.pSetDialogLocation;
        end  % dialog
    end  % constructor block
    
    methods
        function set.CloseAction(obj,value)
        obj.CloseAction = iCheckActionName(value,'CloseAction');
        end
        
        function set.Buttons(obj,value)
        validOptions = {'OK_CANCEL_HELP', 'OK_CANCEL', 'OK_HELP', 'CANCEL_HELP', 'CLOSE', 'CANCEL', 'OK'};
        if ~any( ismember( validOptions, value ) )
            error(message('mbc:xregGui:dialog:InvalidProperty1', 'Buttons', iMakeList( validOptions )));
        end
        obj.Buttons = value;
        end
        
        function set.DefaultAction(obj,value)
        obj.DefaultAction = iCheckActionName(value,'DefaultAction');
        end
        
        function value = get.Name(obj)
        fGet = @(obj,value)getFigureProperty(obj,value,'Name');
        value = fGet(obj,obj.Name);
        end
        function set.Name(obj,value)
        fSet = @(obj,value)setFigureProperty(obj,value,'Name');
        obj.Name = fSet(obj,value);
        end
        
        function value = get.Visible(obj)
        fGet = @(obj,value)getFigureProperty(obj,value,'Visible');
        value = fGet(obj,obj.Visible);
        end
        
        function value = get.Resize(obj)
        fGet = @(obj,value)getFigureProperty(obj,value,'Resize');
        value = fGet(obj,obj.Resize);
        end
        function set.Resize(obj,value)
        fSet = @(obj,value)setFigureProperty(obj,value,'Resize');
        obj.Resize = fSet(obj,value);
        end
        
        function value = get.Tag(obj)
        fGet = @(obj,value)getFigureProperty(obj,value,'Tag');
        value = fGet(obj,obj.Tag);
        end
        function set.Tag(obj,value)
        fSet = @(obj,value)setFigureProperty(obj,value,'Tag');
        obj.Tag = fSet(obj,value);
        end
        
    end   % set and get functions
    
    methods  % public methods
        %----------------------------------------
        function addListeners(obj, L)
        %ADDLISTENERS Convenience method to add listeners to dialog's store.
        %  ADDLISTENERS(OBJ, L) adds the listener vector L to the list of listeners
        %  being held in the component.
        
        obj.Listeners = [obj.Listeners; L(:)];
        
        end  % addListeners
        
        %----------------------------------------
        function closeDialog(obj, mode)
        %CLOSEDIALOG Close the dialog.
        %   CLOSEDIALOG(OBJ, MODE) closes the dialog. The dialog's CloseMode is set
        %   to MODE.
        %
        %   MODE is 'OK', 'CANCEL' or 'CLOSE' depending on how the dialog is
        %   closed.
        %
        %   See also mbcgui.container.Dialog.showDialog.
        if strcmp(mode,'OK') && ~isempty(obj.ValidationFcn) 
            % use validation function to check if dialog is OK before
            % clicking OK
            ptr = obj.Figure.Pointer;
            obj.Figure.Pointer = 'watch';
            drawnow update
            OK = obj.ValidationFcn(); 
            obj.Figure.Pointer = ptr;
            if ~OK
                return
            end
        end
        set(obj.Figure,'CloseMode', mode );
        obj.Figure.Visible = 'off';
        
        end  % closeDialog
        
        %----------------------------------------
        function disableButtons(obj, varargin)
        %DISABLEBUTTONS Disables a dialog's buttons.
        %   DISABLEBUTTONS(OBJ) disables all the dialog's buttons.
        %   DISABLEBUTTONS(OBJ, Name1,Name2) disables the buttons called NAMES. NAMES is
        %   a string, or a cell string.
        %
        %   See also mbcgui.container.Dialog.enableButtons.
        
        actionsToDisable = obj.pGetActions( varargin );
        set( actionsToDisable, 'Enabled', false );
        
        end  % disableButtons
        
        %----------------------------------------
        function enableButtons(obj, varargin)
        %ENABLEBUTTONS Enables a dialog's buttons.
        %   ENABLEBUTTONS(OBJ) enables all the dialog's buttons.
        %   ENABLEBUTTONS(OBJ, Name1,Name2 ) enables the buttons called NAMES. NAMES is
        %   a string,
        %
        %   See also mbcgui.container.Dialog.disableButtons.
        
        
        actionsToDisable = obj.pGetActions( varargin );
        set( actionsToDisable, 'Enabled', true );
        
        end  % enableButtons
        
        %----------------------------------------
        function resetPointer(obj)
        %RESETPOINTER Reset the pointer to previous setting.
        %   RESETPOINTER(OBJ)
        %
        %   See also mbcgui.container.Dialog.setPointer.
        
        PR = xregGui.PointerRepository;
        PR.stackRemovePointer( obj.Figure, obj.PointerID(end) );
        obj.PointerID(end) = [];
        drawnow('expose');
        
        end  % resetPointer
        
        %----------------------------------------
        function setPointer(obj, pointer)
        %SETPOINTER Set the dialog's pointer
        %
        %   SETPOINTER(OBJ, POINTER).
        %
        %   See also mbcgui.container.Dialog.resetPointer.
        
        PR = xregGui.PointerRepository;
        obj.PointerID(end+1) = PR.stackSetPointer( obj.Figure, pointer );
        drawnow('expose');
        
        end  % setPointer
        
        %----------------------------------------
        function status = showDialog(obj)
        %SHOWDIALOG Show the dialog
        %
        %   STATUS = SHOWDIALOG(OBJ) opens the dialog and blocks execution until it
        %   is closed or until OK or Cancel is pressed.
        %
        %   Immediately after the dialog is made visible but before it code
        %   execution is blocked, the 'DialogShowing' event is sent.  If you want
        %   to execute code after the dialog is visible then you can use a listener
        %   on this event.
        %
        %   See also mbcgui.container.Dialog.closeDialog.
        
        
        % Make sure the figure is in the correct position.
        % Do this first so the everything else gets the final figure size.
        obj.pSetDialogLocation();
        
        % We actually make everything here.
        obj.pLayout();
        
        % Add a figure key action to implement Esc capturing.
        obj.Figure.WindowKeyReleaseFcn = @i_catchEsc;
        
        % Add a highlighter for the default action.  This also takes care of
        % handling Enter presses
        DefaultActionButton = obj.ButtonHandles.(obj.DefaultAction);
        obj.Figure.setDefaultButton(DefaultActionButton);
        
        % Set the CloseRequestFcn.
        obj.Figure.CloseRequestFcn = {@iFigureCloseRequest, obj.Actions.(obj.CloseAction) };
        
        % And make it visible.
        obj.Figure.CloseMode = '';
        obj.Figure.Visible = 'on';
        
        % Send an event to allow dialog creators to execute custom code after the
        % dialog is made visible
        obj.notify('DialogShowing');
        
        TESTMODE = obj.Tester.Mode;
        if ~TESTMODE
            waitfor(obj.Figure, 'Visible');
        elseif TESTMODE
            callback = obj.Tester.ShowDialogCallback;
            if ~isempty(callback)
                xregcallback( callback, obj.Figure, DefaultActionButton );
            else
               obj.Figure.CloseMode = obj.CloseAction;
            end
        end
        
        % Return the CloseMode
        status = obj.Figure.CloseMode;
        
        % And hide the figure.
        obj.Figure.Visible = 'off';
        end  % showDialog
        
    end  % public methods
    
    
    methods (Hidden) % possibly private or hidden
        %----------------------------------------
        function buttons = pCreateButtons(obj)
        %PCREATEBUTTONS Private method to create the Buttons.
        %
        %   BUTTONS = PCREATEBUTTONS(OBJ) Creates the buttons specified but the
        %   Buttons property and returns a cell array of button handles in the
        %   order they should be laid out (left to right). Also sets the dialog
        %   CloseAction, again based on the Buttons property.
        
        
        % Here we translate the 'enumerated' Buttons [property to the names of the
        % actions we need buttons for.  The order of the names is the order the
        % buttons will be in.
        switch obj.Buttons
            case 'OK_CANCEL_HELP'
                actionNames = {'OK', 'CANCEL', 'HELP'};
                obj.CloseAction = 'CANCEL';
                defaultAction = 'OK';
            case 'OK_CANCEL'
                actionNames = {'OK', 'CANCEL'};
                obj.CloseAction = 'CANCEL';
                defaultAction = 'OK';
            case 'OK_HELP'
                actionNames = {'OK', 'HELP'};
                obj.CloseAction = 'OK';
                defaultAction = 'OK';
            case 'CANCEL_HELP'
                actionNames = {'CANCEL', 'HELP'};
                obj.CloseAction = 'CANCEL';
                defaultAction = 'CANCEL';
            case 'CANCEL'
                actionNames = {'CANCEL'};
                obj.CloseAction = 'CANCEL';
                defaultAction = 'CANCEL';
            case 'CLOSE'
                actionNames = {'CLOSE'};
                obj.CloseAction = 'CLOSE';
                defaultAction = 'CLOSE';
            case 'OK'
                actionNames = {'OK'};
                obj.CloseAction = 'OK';
                defaultAction = 'OK';
            otherwise
                % We should never get here - the check on the property set should
                % have errored if the value is invalid.
                error(message('mbc:xregGui:dialog:InvalidOption', obj.Buttons));
        end
        
        % If no DefaultAction has been specified use the calculated default.
        if isempty( obj.DefaultAction )
            obj.DefaultAction = defaultAction;
        else
            % check the user provided DefaultAction is one of the actions we have a
            % button for.
            if ~ismember( obj.DefaultAction, actionNames )
                error(message('mbc:xregGui:dialog:InvalidProperty', obj.DefaultAction, obj.Buttons));
            end
        end
        
        numButtons = length(actionNames);
        buttons = cell(1, numButtons);
        for i=1:numButtons
            name = actionNames{i};
            buttons{i} = obj.Actions.(name).createButton( obj.Figure );
            % set tag
            buttons{i}.Tag = name;
            obj.ButtonHandles.(name) = buttons{i};
        end
        end  % pCreateButtons
        
        %----------------------------------------
        function actions = pGetActions(obj, names)
        %PGETACTIONS Private method to get the actions from the dialog.
        %
        %   ACTIONS = PGETACTIONS(OBJ, NAMES)
        
        
        if nargin==1
            actions = struct2cell( obj.Actions );
        else
            if ischar( names )
                names = {names};
            end
            actions = cellfun( @(n)obj.Actions.(n), names, 'UniformOutput', false );
        end
        actions = [actions{:}];
        
        end  % pGetActions
        
        %----------------------------------------
        function pLayout(obj)
        %PLAYOUT Private method to layout the dialog.
        %
        %   PLAYOUT(OBJ)
        
        if isempty(obj.ContentLayout)
            % Create a layout that applies the content border
            obj.ContentLayout = xreglayerlayout( obj.Figure,...
                'PackStatus', 'off');
        end
        set(obj.ContentLayout,...
            'Elements', {obj.Content},...
            'Border', obj.ContentBorder );
        
        
        % Create the appropriate buttons
        buttons = obj.pCreateButtons();
        numBtns = length( buttons );
        
        % Delete any existing buttons and the layout they are in
        if ~isempty(obj.ButtonLayout)
            % Detach the content layout from the existing grid.
            old_elements = get(obj.ButtonLayout, 'Elements');
            old_elements{1} = [];
            set(obj.ButtonLayout, 'Elements', old_elements);
            delete(obj.ButtonLayout);
        end
        
        % Create a layout that adds the buttons
        elements = [{obj.ContentLayout}, repmat( {[]}, 1, numBtns+2 );...
            {[]}, {[]} buttons(:)', {[]} ];
        obj.ButtonLayout = xreggridbaglayout( obj.Figure,...
            'PackStatus', 'off',...
            'Dimension', [2 numBtns+3],...
            'RowSizes', [-1 25],...
            'ColSizes',[0 -1 repmat( 65, 1, numBtns ), 0],...
            'Gapx',7,...
            'Gapy', 7,...
            'Border',[0 7 0 0],...
            'mergeblock', {[1 1],[1 numBtns+3]},...
            'Elements', elements);
        
        TopLayout = obj.ButtonLayout;
        
        % Create a layout for the dialog information only if it is required
        HasInfo = ~isempty(obj.InfoTitle) || ~isempty(obj.InfoString);
        if HasInfo && isempty(obj.InfoPane)
            obj.InfoPane = mbcgui.container.InfoPane(...
                'Parent', obj.Figure, ...
                'Visible', 'off');
        end
        if ~isempty(obj.InfoPane)
            set(obj.InfoPane, 'Title', obj.InfoTitle, ...
                'InfoString', obj.InfoString, ...
                'InfoHeight', obj.InfoHeight);
        end
        
        % In order to make sure the info pane repacks at the right time we need to
        % make the figure's packstatus is on here.
        set( obj.ButtonLayout, 'PackStatus', 'on');
        
        if HasInfo
            % Use the InfoPane as the figure's main layout
            set(obj.InfoPane, 'Center', TopLayout, 'Visible', 'on');
            TopLayout = obj.InfoPane;
        else
            set(obj.InfoPane, 'Center', [], 'Visible', 'off');
        end
        
        obj.Figure.LayoutManager = [];
        obj.Figure.LayoutManager = TopLayout;
        set(obj.ButtonLayout, 'Visible', 'on' );
        
        end  % pLayout
        
        %----------------------------------------
        function pSetDialogLocation(obj)
        %PSETDIALOGLOCATION Private method to set the dialog's location.
        %
        %   PSETDIALOGLOCATION(OBJ)
        
        if ~isempty( obj.Figure )
            % Work out the default position we want to use
            scr=get(0,'ScreenSize');
            if ~isempty( obj.Owner )
                figpos=get(obj.Owner,'Position');
                defpos=[figpos(1:2)+(figpos(3:4)-obj.Size)./2 obj.Size];
            else
                defpos=[(scr(3:4)-obj.Size)./2 obj.Size];
            end
            
            % Set position
            if obj.PersistPosition
                xregpersistfigpos( obj.Figure, 'DefaultPos', defpos );
            else
                set(obj.Figure, 'Position', defpos);
            end
            
            % Move figure to be entirely on screen
            xregmoveonscreen( obj.Figure );
        end
        
        end  % pSetDialogLocation
        
        %----------------------------------------
        function pUpdateInfo(obj, InfoProp)
        %PUPDATEINFO Private method to update the dialog information.
        %
        %   PUPDATEINFO(OBJ, PROP) is called when dialog information properties are
        %   altered.
        
        
        % New information is only set if the infopane has been created.  If no
        % information was set when the dialog is shown, setting information will
        % not cause it to appear.  This is deliberate behaviour and prevents
        % dialogs that rearrange their contents in disconcerting ways.
        if ~isempty(obj.InfoPane)
            InfoPaneProp = InfoProp;
            
            % Convert this object's InfoTitle property to Title for the infopane
            InfoPaneProp = regexprep(InfoPaneProp, '^InfoTitle$', 'Title');
            
            obj.InfoPane.(InfoPaneProp) = obj.(InfoProp);
        end
        
        end  % pUpdateInfo
        
    end  % possibly private or hidden
    
end  % classdef

function value = setFigureProperty( obj, value, property )
if ~isempty( obj.Figure )
    obj.Figure.(property) = value;
end
end  % setFigureProperty


function value = getFigureProperty( obj, value, property )
if ~isempty( obj.Figure )
    value = obj.Figure.(property);
end
end  % getFigureProperty


function value = iCheckActionName( value, propertyName )
validOptions = {'OK', 'CANCEL', 'CLOSE', 'HELP'};
if ~any( ismember( validOptions, value ) )
    error(message('mbc:xregGui:dialog:InvalidProperty2', propertyName, iMakeList( validOptions )));
end
end  % iCheckActionName


function validList = iMakeList( validOptions )
validList = sprintf( '%s, ', validOptions{1:end-1} );
validList = sprintf( '%s or %s', validList(1:end-2), validOptions{end} );
end  % iMakeList

function action = iMakeAction( dialog, callback, name )
action = mbcgui.container.DialogAction( callback, name );
action.Dialog = dialog;
end  % iMakeAction


function iDeleteFigure( obj )
if isgraphics( obj.Figure )
    delete(obj.Figure)
end
end  % iDeleteFigure


function iShowHelp( obj )
mv_helptool( obj.HelpCode, obj.Figure );
end  % iShowHelp


function iUpdateInfo(src, evt)
obj = evt.AffectedObject;
PropName = src.Name;
pUpdateInfo(obj, PropName);
end  % iUpdateInfo

function i_catchEsc(src, ~)
if get(src, 'CurrentCharacter')==27
    close(src);
end
end  % i_catchEsc


function iFigureCloseRequest( ~, ~, closeAction )
closeAction.execute();
end  % iFigureCloseRequest