classdef Dialog < handle
    % Concrete class for constructing dialogs.
    % Dialog instances are intended to be used with DialogPresenter objects
    % to visualize the dialog content.  Dialogs combine DialogBorder and
    % DialogContent to provide complete a dialog description.

        
%   Copyright 2010-2014 The MathWorks, Inc.

    properties (SetAccess=private)
        % Installed at object instantiation
        DialogBorder    % type dialogmgr.DialogBorder
        DialogContent   % type dialogmgr.DialogContent
        
        Name            % copied from DialogContent
        Tag             % copied from DialogContent
        
        % Installed when create() called
        DialogPresenter % type dialogmgr.DialogPresenter
        
        % List of uidialog children used in creating this Dialog
        ChildDialogs = dialogmgr.Dialog.empty
        ParentDialog = dialogmgr.Dialog.empty
    end
    
    methods
        function dlg = Dialog(dlgContent,dlgBorder)
            % Dialog(dlgContent,hBorder) creates a Dialog object with
            % DialogContent object hContent and DialogBorder object
            % hBorder.
            %
            % Dialog(dlgContent) uses dialogmgr.DBCompact as the default
            % DialogBorder object.
            
            mlock;
            
            if nargin<1
                dlgContent = dialogmgr.DCEmpty;
            end
            sigdatatypes.checkIsA(dlg,'DialogContent', ...
                dlgContent,'dialogmgr.DialogContent');
            dlg.DialogContent = dlgContent;
            
            %ADDED
            if ~isempty(dlgContent) && ~isempty(dlgContent.Dialog)
                dlg.ChildDialogs = dlgContent.Dialog.ChildDialogs;
            end
            
            if nargin<2
                dlgBorder = dialogmgr.DBCompact;
            end
            sigdatatypes.checkIsA(dlg,'DialogBorder', ...
                dlgBorder,'dialogmgr.DialogBorder');
            dlg.DialogBorder = dlgBorder;
            
            % Copy properties for convenience
            dlg.Name = dlgContent.Name;
            dlg.Tag  = dlgContent.Tag;
        end
        
        function warn(dlg,warnType)
            % Issue common dialog warnings
            
            switch lower(warnType)
                % Internal message to help debugging. Not intended to be user-visible.
              case 'dialognotregistered'
                warning(message('Spcuilib:dialogmgr:WarnDialogNotRegistered', dlg.Name));
              case 'duplicatedialogname'
                warning(message('Spcuilib:dialogmgr:WarnDuplicateDialogName', dlg.Name));
              otherwise
                warning(message('Spcuilib:dialogmgr:WarnUndocumentedDialog', dlg.Name));
            end
        end
        
        function select(dlg,state)
            % Select a dialog.
            %
            % select(dlg) selects a dialog, which generally causes a
            % visual highlight to appear.
            %
            % select(dlg,state) selects a dialog when state is true, or
            % de-selects when false.
            
            % Pass message to border
            if ~isempty(dlg.DialogBorder)
                select(dlg.DialogBorder,state);
            end
        end

        function cacheTraversalInfo(dlgs,doCache)
            % In each dialog that is a child dialog, fill in or clear:
            %   .Dialog.ChildDialogs
            %   .Dialog.ParentDialog
            %   .DialogContent.Dialog
            %
            % Couldn't have been done in DP:register() method, since while
            % the dialogs register, we don't have the backtrack info we
            % need in .Dialogs, so we do it here.
            
            if nargin<2 || doCache
                % Cache dialog handles
                for i = 1:numel(dlgs)
                    d_i = dlgs(i);
                    if isDPChildDialog(d_i.DialogPresenter)
                        parent_d = up_precache(d_i);
                        
                        % Add to child dialog list
                        parent_d.ChildDialogs(end+1) = d_i;
                        
                        % Set parent dialog
                        d_i.ParentDialog = parent_d;
                    end
                    
                    % Set Dialog into DialogContent
                    d_i.DialogContent.Dialog = d_i;
                end
            else
                % Un-cache dialog handles
                for i = 1:numel(dlgs)
                    d_i = dlgs(i);
                    if isDPChildDialog(d_i.DialogPresenter)
                        d_i.ParentDialog.ChildDialogs = [];
                        d_i.ParentDialog = [];
                    end
                    d_i.DialogContent.Dialog = [];
                end
            end
        end
        
        function parent_dlg = up_precache(dlg)
            % Return parent Dialog object.
            %
            % Returns empty if no parent or no parent dialog with matching
            % name.
            
            % We have no direct access to the parent Dialog!
            % Need to get name of parent dialog,
            % then match it up in parent DP Dialogs
            
            dp = dlg.DialogPresenter;
            if isDPChildDialog(dp)
                dc = dp.ParentDialogContent;
                parent_dlgs = dc.DialogPresenter.Dialogs;
                sel = strcmpi(dc.Name,{parent_dlgs.Name});
                parent_dlg = parent_dlgs(sel); % works for ~any(sel)
            else
                % No parent dialog unless this is a child
                parent_dlg = [];
            end
        end
        
        function parent_dlg = up(dlg)
            % Return parent Dialog object, or empty is not a child.
            % If a vector of dialogs present, just use the first, as parent
            % is unambiguous and leads to the same result
            
            persistent top_most
            
            parent_dlg = dlg(1).ParentDialog;
            
            if isempty(parent_dlg)
                % We are at top-most dialog, parent is "top-most DP"
                % really.  
                if isempty(top_most)
                    top_most = dialogmgr.Dialog;
                    top_most.Name = 'TOP';
                    top_most.ParentDialog = top_most; % point to self
                    top_most.ChildDialogs = dlg.DialogPresenter.Dialogs;
                end
                parent_dlg = top_most;
            end
        end
        
        function child_dlg = down(dlg,child_name)
            % Return named child Dialog object by reference to its .Tag
            % string, or empty if no children or no named child exists.
            %
            % Do NOT throw an error for a dialog that cannot be identified
            % by name; the dialog may not be present, and we don't want to
            % halt the dialog system in that case.
            %
            % The .Name string of the dialog may be used if Tag is not
            % specified.  Tag is preferred as Name may be translated for
            % display purposes.
            
            assert(isscalar(dlg));
            
            child_dlgs = dlg.ChildDialogs;
            if isempty(child_dlgs)
                child_dlg = [];
                return
            end
            
            % Try matching .Tag strings of child dialogsfirst
            sel = strcmpi(child_name,{child_dlgs.Tag});
            if ~any(sel)
                % If no .Tag matches, try matching .Name
                sel = strcmpi(child_name,{child_dlgs.Name});
            end
            child_dlg = child_dlgs(sel); % works for ~any(sel)
        end
    end
    
    methods (Sealed)
        % Services that cannot be overridden
        
        function initialize(h,dp)
            % Create dialog and content.
            % Caller must provide a valid DialogPresenter context at the
            % time of widget creation.
            
            % Protect against repeated calls by checking h.DialogPresenter
            % Cached property value should be empty at this point.
            %if ~isempty(h.DialogPresenter)
                % Internal message to help debugging. Not intended to be user-visible.
            %    error(message('Spcuilib:dialogmgr:ErrorDialogNotFinalized'));
            %end
            
            % Test and cache DialogPresenter
            sigdatatypes.checkIsA(h,'DialogPresenter', ...
                dp,'dialogmgr.DialogPresenter');
            h.DialogPresenter = dp;
            
            % Responsible for creating a top-level panel to which all
            % dialog content will be parented.
            %
            % This is an abstract method implemented by a subclass,
            % typically another abstract class that provides a certain type
            % of graphical dialog (a concrete look-and-feel) but not the
            % content of a particular final dialog.
            initialize(h.DialogBorder,dp);
            
            % Create widgets specific to the Dialog subclass
            %
            % This is an abstract method implemented by a subclass.
            initialize(h.DialogContent,h.DialogBorder);
            
            % Complete dialog border services, such as border name or
            % autosizing of panels.
            %
            % This is NOT abstract, so frameworks that do not need this can
            % choose not to provide an override.
            update(h.DialogBorder,h.DialogContent);
        end
        
        function update(h)
            % Update DialogContent within Dialog.
            % Suppresses execution if dialog is not visible.
            %
            N = numel(h);
            for i = 1:N
                if isDialogVisible(h(i).DialogPresenter,h(i).Name)
                    update(h(i).DialogContent);
                end
            end
        end
        
        function finalizeForUndock(h)
            % Finalize Dialog object, but don't touch DialogContent since
            % it will be re-docked to another Dialog and dialogBorder
            % later.
            
            % Release but do not destroy:
            h.DialogContent = [];
            
            finalize(h.DialogBorder);
            h.DialogBorder = [];
            
            % Release but do not destroy:
            h.DialogPresenter = [];
        end
        
        function finalize(h)
            
            finalize(h.DialogContent);
            h.DialogContent = [];
            
            finalize(h.DialogBorder);
            h.DialogBorder = [];
            
            % Release but do not destroy:
            h.DialogPresenter = [];
        end
        
        function setVisible(thisDialog,state)
            % state may be 'on' or 'off', or true or false
            if nargin<2
                state='on';
            end
            setVisible(thisDialog.DialogBorder,state);
        end
        
        function disableRollerShadeIfAvailable(dlg)
            % When we close a dialog, we turn off RollerShade if it is enabled
            % This provides a uniform experience when opening dialogs:
            %   they are always "unrolled" when opened.
            %
            dialogBorder = dlg.DialogBorder;
            if hasService(dialogBorder,'DialogRoller')
                % disable roller shade after dialog has been made invisible
                dialogBorder.RollerShade = false;
            end
        end
        
        function disableRollerShadeIfAvailable_NoAnimation(dlg)
            dialogBorder = dlg.DialogBorder;
            origAnim = dialogBorder.Animation; % hidden property
            if origAnim
                dialogBorder.Animation = false;
            end
            disableRollerShadeIfAvailable(dlg);
            if origAnim
                dialogBorder.Animation = origAnim;
            end
        end
        
        function ID = getID(thisDialog)
            % Returns the unique ID assigned to each instance of a
            % DialogContent object.  Works for a vector of dialog objects.
            dc = [thisDialog.DialogContent]; % homog. vector of DC objects
            if isempty(dc)
                ID = [];
            else
                ID = [dc.ID]; % vector of integer IDs
            end
        end
        
        function setWidth(dlg,W)
            % Width of Dialog is the outer width of DialogBorder, inclusive
            % of any decorative inner borders that DialogBorder creates.
            setOuterWidth(dlg.DialogBorder,W);
        end
        
        function setYPos(dlg,Y)
            setYPos(dlg.DialogBorder,Y);
        end
        
        function changeDialogContentHeight(dlgs)
            % Call changeDialogContentHeight on each dialog in the vector.
            N = numel(dlgs);
            for i = 1:N
                changeDialogContentHeight(dlgs(i).DialogContent);
            end
        end
        
    end
end

