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

    classdef MultiViewPanel < mbcgui.widget.BasicContainer
    %mbcgui.multiview.MultiViewPanel class
    %   mbcgui.multiview.MultiViewPanel extends mbcgui.widget.BasicContainer.
    %
    %    mbcgui.multiview.MultiViewPanel properties:
    %       Parent - Property is of type 'MATLAB array'
    %       Position - Property is of type 'rect'
    %       Enable - Property is of type 'on/off'
    %       Visible - Property is of type 'on/off'
    %       UserData - Property is of type 'MATLAB array'
    %       Tag - Property is of type 'string'
    %       Actions - Property is of type 'MATLAB array' (read only)
    %       MessageService - Property is of type 'handle'
    %       ViewList - Property is of type 'handle'
    %       ViewLayoutName - Property is of type 'string'
    %       StatusBar - Property is of type 'handle'
    %       GlobalViewOptions - Property is of type 'handle vector'
    %       ContextActions - Property is of type 'handle vector'
    %       AlwaysAllowPrintToFigure - Property is of type 'bool'
    %
    %    mbcgui.multiview.MultiViewPanel methods:
    %       addViewMenuItems - Add standard view management items to a menu
    %       canPrint - Check whether component can be printed
    %       changeView - Change the selected view to a different one
    %       deleteView - Delete the currently selected view
    %       getSelectedViewContainer - Get the handle of the selected ViewContainer
    %       getSelectedViewTypeIndex - Get the index of the type of the selected view
    %       getViewContainers - AGet all of the open ViewContainers
    %       initViewLayout - Create a default view layout
    %       printSize - Returns the preferred printing size for the component
    %       printToFigure - Create a copy of the current view in a new figure.
    %       printCopy - Create a new component for printing the component display
    %       restoreViewLayout - Restore a saved layout
    %       saveViewLayout - Save the current view layout
    %       selectView - Select a view
    %       splitView - Split the currently selected view
    %       updateAvailableViews - Re-check which views can be created
    
    %  Copyright 2005-2016 The MathWorks, Inc. and Ford Global Technologies, Inc.

    properties (AbortSet, SetObservable)
        %MESSAGESERVICE Property is of type 'handle'
        MessageService = [];
        %VIEWLIST Property is of type 'handle'
        ViewList = [];
        %VIEWLAYOUTNAME Property is of type 'string'
        ViewLayoutName = '';
        %STATUSBAR Property is of type 'handle'
        StatusBar = [];
        %ALWAYSALLOWPRINTTOFIGURE Property is of type 'bool'
        AlwaysAllowPrintToFigure = false;
    end
    
    properties(AbortSet, Dependent)
        %GLOBALVIEWOPTIONS Property is of type 'handle vector'
        GlobalViewOptions = [];
        %CONTEXTACTIONS Property is of type 'handle vector'
        ContextActions = [];
    end
    
    properties(SetAccess=private, Dependent)
        %SelectedView selected view 
        SelectedView
    end
    
    properties (SetAccess=protected, AbortSet)
        %VIEWGROUP Property is of type 'handle'
        ViewGroup = [];
        %HTOPLAYOUT Property is of type 'MATLAB array'
        hTopLayout = [];
        %HVIEWCONTEXTMENU Property is of type 'MATLAB array'
        hViewContextMenu = [];
        %GLOBALVIEWOPTIONGROUP Property is of type 'handle'
        GlobalViewOptionGroup = [];
        %CONTEXTACTIONGROUP Property is of type 'handle'
        ContextActionGroup = [];
    end
    
    properties (SetAccess=protected, AbortSet)
        %ACTIONS Property is of type 'MATLAB array' (read only)
        Actions = [];
    end
    
    methods  % constructor block
        function obj = MultiViewPanel(varargin)
        %MULTIVIEWPANEL Construct a new MultiViewPanel
        %  OBJ = MULTIVIEWPANEL(PROP, VALUE, ...) constructs a new MultiViewPanel.
        %  This component contains a set of Views housed within ViewContainers
        %  that are arranged in a hierarchy of splitlayouts.
        
        obj@mbcgui.widget.BasicContainer(varargin{ : }); % converted super class constructor call
        
        VG = mbcgui.multiview.ViewGroup;
        obj.ViewGroup = VG;
        obj.addListeners([...
            event.proplistener(VG, VG.findprop('ViewContainers'), ...
            'PostSet',@(h,evt) i_checkcloseable(h,evt,obj)); ...
            event.listener(VG, 'SelectedViewChanged', @(h,evt) i_selviewchange(h,evt,obj))
            ]...
            );
        
        % Create an empty ViewList if one has not been set
        if isempty(obj.ViewList)
            obj.ViewList = mbcgui.multiview.ViewList;
        end
        
        % Actions the UI can perform
        A.CloseView = mbcgui.actions.StatefulAction(...
            {@i_closeview, obj}, '&Close View','Close current view');
        
        A.SplitViewV = mbcgui.actions.StatefulAction(...
            {@i_split, obj, 'ud'}, 'Split View &Vertically', ...
            'Split current view vertically', xregrespath('verticalSplit.bmp'));
        
        A.SplitViewH = mbcgui.actions.StatefulAction(...
            {@i_split, obj, 'lr'}, 'Split View &Horizontally', ...
            'Split current view horizontally', xregrespath('horizontalSplit.bmp'));
        
        A.CopyView = mbcgui.actions.StatefulAction(...
            {@i_printview, obj, 'clipboard'}, '&Copy View', ...
            'Copy current view to clipboard', xregrespath('copy.bmp'));
        
        A.PrintView = mbcgui.actions.StatefulAction(...
            {@i_printview, obj, 'printdialog'}, '&Print...', ...
            'Print current view', xregrespath('print.bmp'));
        
        A.PrintViewQuiet = mbcgui.actions.StatefulAction(...
            {@i_printview, obj, 'printer'}, '&Print', ...
            'Print current view', xregrespath('print.bmp'));
        
        A.PrintViewPreview = mbcgui.actions.StatefulAction(...
            {@i_printview, obj, 'printpreview'}, 'Print Pre&view','Print preview');
        
        A.PrintViewFigure = mbcgui.actions.StatefulAction(...
            {@i_printtofig, obj}, 'Print To &Figure', ...
            'Print to figure', xregrespath('printtofigure.bmp'));
        
        A.ChangeView = obj.pCreateViewListActions('change');
        
        A.SplitToView = obj.pCreateViewListActions('split');
        
        A.ViewOptions = mbcgui.actions.DynamicActionGroup('', 'View &Options', 'Current view options');
        A.ViewOptions.MenuType = 'none';
        
        A.ViewActions = mbcgui.actions.DynamicActionGroup('', 'View &Actions', 'Current view actions');
        A.ViewActions.MenuType = 'separate';
        
        obj.Actions = A;
        
        if isempty(obj.GlobalViewOptionGroup)
            obj.GlobalViewOptionGroup = mbcgui.actions.DynamicActionGroup('', 'Common View Options', 'Additional view options');
            obj.GlobalViewOptionGroup.MenuType = 'none';
        end        
        if isempty(obj.ContextActionGroup)
            obj.ContextActionGroup = mbcgui.actions.DynamicActionGroup('', 'Additional Actions', 'Context menu actions');
            obj.ContextActionGroup.MenuType = 'separate';
        end
        
        % Create the context menu
        obj.hViewContextMenu = uicontextmenu('Parent', ancestor(obj.Parent, 'figure'));
        obj.pCreateContextMenu(obj.hViewContextMenu);
        
        % Create the container that will house the splittable views
        obj.hTopLayout = xreglayerlayout(obj.Parent, ...
            'Position', obj.Position, ...
            'Packstatus', 'off');
        
        % Create the rest of the component's layout
        obj.ContentHandle = pCreateLayout(obj, obj.hTopLayout);
        
        % Create the initial set of view objects
        if obj.pHasSavedLayout
            obj.restoreViewLayout(obj.ViewLayoutName);
        else
            obj.initViewLayout;
        end
        
        set(obj.hTopLayout, 'packstatus', 'on');
        
        % Enable the correct set of actions
        obj.pUpdateActions;
        
        end  % MultiViewPanel
        
    end  % constructor block
    
    methods
        function set.MessageService(obj,value)
        % DataType = 'handle'
        validateattributes(value,{'handle'}, {'scalar'},'','MessageService')
        obj.MessageService = value;
        
        hVC = obj.getViewContainers;
        if ~isempty(hVC)
            hV = get(hVC, {'View'});
            hV = [hV{:}];
            set(hV, 'MessageService', obj.MessageService);
        end
        
        end
        
        function set.GlobalViewOptions(obj,value)
        % DataType = 'handle vector'
        
        if isempty( obj.GlobalViewOptionGroup )
            obj.GlobalViewOptionGroup = mbcgui.actions.DynamicActionGroup('', 'Common View Options', 'Additional view options');
            obj.GlobalViewOptionGroup.MenuType = 'none';
        end
        obj.GlobalViewOptionGroup.Actions = value;
        end
        
        function value = get.GlobalViewOptions(obj)
        if ~isempty( obj.GlobalViewOptionGroup )
            value = obj.GlobalViewOptionGroup.Actions;
        else
            value = [];
        end
        end
        
        function set.ContextActions(obj,value)
        % DataType = 'handle vector'
        
        if isempty(obj.ContextActionGroup)
            obj.ContextActionGroup = mbcgui.actions.DynamicActionGroup('', 'Additional Actions', 'Context menu actions');
            obj.ContextActionGroup.MenuType = 'separate';
        end
        
        obj.ContextActionGroup.Actions = value;
        end        
        
        function value = get.ContextActions(obj)
        value = obj.ContextActionGroup.Actions;
        end
        
        function v = get.SelectedView(obj)
        c = obj.getSelectedViewContainer;
        v = c.View;
        end
        
    end   % set and get functions
    
    methods  % public methods
        function addViewMenuItems(obj, hMenu)
        %ADDVIEWMENUITEMS Add standard view management items to a menu
        %  ADDVIEWMENUITEMS(OBJ, HMENU) adds  the standard set of view management
        %  menu items to the parent menu HMENU.  These menus allow the user to
        %  change views, split views, delete views, access view options and copy
        %  views.
        
        A = obj.Actions;
        
        Mchange = A.ChangeView.createMenuItem(hMenu);
        
        % Add separator if view changers are not the first menu item
        if length(get(hMenu, 'Children'))>1
            set(Mchange(1), 'Separator', 'on');
        end
        
        A.ViewOptions.createMenuItem(hMenu);
        obj.GlobalViewOptionGroup.createMenuItem(hMenu);
        m = A.SplitToView.createMenuItem(hMenu);
        set(m(1), 'Separator', 'on');
        A.SplitViewV.createMenuItem(hMenu);
        A.SplitViewH.createMenuItem(hMenu);
        A.CloseView.createMenuItem(hMenu);
        m = A.PrintViewFigure.createMenuItem(hMenu);
        set(m, 'Separator', 'on');
        
        end  % addViewMenuItems
        
        function out = canPrint(obj)
        %CANPRINT Check whether component can be printed
        %  CANPRINT(OBJ) calls canPrint on the selected view.
        
        if ~isempty(obj.ViewGroup.SelectedViewContainer)
            out = obj.ViewGroup.SelectedViewContainer.canPrint;
        else
            out = false;
        end
        
        end  % canPrint
        
        function changeView(obj, ViewIndex)
        %CHANGEVIEW Change the selected view to a different one
        %  CHANGEVIEW(OBJ, VIEWINDEX) changes the selected view to one of type
        %  VIEWINDEX.
        
        hVC = obj.ViewGroup.SelectedViewContainer;
        if ~isempty(hVC)
            if obj.ViewList.isViewAvailable(obj.MessageService, ViewIndex);
                P = xregGui.PointerRepository;
                PtrID = P.stackSetPointer(obj.Parent, 'watch');
                MsgID = obj.pAddStatusMessage('Changing current view...');
                
                hNewVC = obj.pCreateView(ViewIndex);
                obj.ViewGroup.selectView(hNewVC);
                VCdata = get(hVC,'UserData');
                
                replace(VCdata{1}, hNewVC, VCdata{2});
                delete(hVC);
                
                hNewVC.UserData = VCdata;
                hNewVC.Visible = obj.Visible;
                
                obj.pRemoveStatusMessage(MsgID);
                P.stackRemovePointer(obj.Parent, PtrID);
            else
                errordlg(['This view is currently not available.  ', ...
                    'Please select a different view.'], ...
                    'MBC Toolbox', 'modal');
            end
        end
        
        end  % changeView
        
        function deleteView(obj)
        %DELETEVIEW Delete the currently selected view
        %  DELETEVIEW(OBJ) deletes the currently selected view so long as there is
        %  more than one view present.
        
        if length(obj.ViewGroup.ViewContainers)>1
            P = xregGui.PointerRepository;
            PtrID = P.stackSetPointer(obj.Parent, 'watch');
            MsgID = obj.pAddStatusMessage('Closing current view...');
            
            hVC = obj.ViewGroup.SelectedViewContainer;
            
            VCdata = get(hVC,'UserData');
            hParent = VCdata{1};
            delIndex = VCdata{2};
            keepIndex = 3-delIndex;
            ParentData = get(hParent, 'UserData');
            el = get(hParent, 'elements');
            hVCKeep = el{keepIndex};
            set(hVCKeep, 'UserData', ParentData);
            
            replace(ParentData{1}, hVCKeep, ParentData{2});
            
            set(hParent,'elements', {});
            delete(hParent);
            delete(hVC);
            
            obj.pRemoveStatusMessage(MsgID);
            P.stackRemovePointer(obj.Parent, PtrID);
        end
        
        end  % deleteView
        
        function hasView = enableView(obj,viewClass,status)
        %enableView enable views
        %    enableView(obj,viewClass,status)
        
        indexClass = findViewClass(obj.ViewList,viewClass);
        if ~isempty(indexClass)
            hasView = obj.ViewList.NumCreated(indexClass)>0;
            enableView(obj.ViewList,indexClass,status);
            ViewActions = obj.Actions;
            ViewActions.ChangeView.Actions(indexClass).Enabled = status;
            ViewActions.SplitToView.Actions(indexClass).Enabled = status;
        end
        
        end
        
        function generateReport(obj,rpt,lvl)
        %generateReport generate report for multiview
        %    generateReport(obj,rpt,lvl)
        
        if nargin<3
            lvl = 2;
        end
        
        el = get(obj.hTopLayout, 'elements');
        if ~isempty(el)
            generateViewReport(el{1})
        end
        
            function generateViewReport(hL)
            %generateViewReport recurse through views 
            if isa(hL,'xregsplitlayout')
               generateViewReport(get(hL,'left')); 
               generateViewReport(get(hL,'right')); 
            else
                generateReport(hL.View,rpt,lvl);
            end
            
            end
        
        end
        
        function hVC = getSelectedViewContainer(obj)
        %GETSELECTEDVIEWCONTAINER Get the handle of the selected ViewContainer
        %  HVC = GETSELECTEDVIEWCONTAINER(OBJ) returns the handle of the
        %  ViewContainer that is selected.
        
        hVC = obj.ViewGroup.SelectedViewContainer;
        
        end  % getSelectedViewContainer
        
        function idx = getSelectedViewTypeIndex(obj)
        %GETSELECTEDVIEWTYPEINDEX Get the index of the type of the selected view
        %  IDX = GETSELECTEDVIEWTYPEINDEX(OBJ) returns the index into the current
        %  ViewList of the currently selected view.
        
        hVC = obj.getSelectedViewContainer;
        data = hVC.MVP_Data;
        lbl = data.ConstructorLabel;
        idx = obj.ViewList.findViewLabel(lbl);
        
        end  % getSelectedViewTypeIndex
        
        function hVC = getViewContainers(obj)
        %GETVIEWCONTAINERS AGet all of the open ViewContainers
        %  GETVIEWCONTAINERS(OBJ) returns a handle vector containing all of the
        %  ViewContainers that are in the layout.
        
        if ~isempty( obj.ViewGroup)
            hVC = obj.ViewGroup.ViewContainers;
        else
            hVC = [];
        end
        
        end  % getViewContainers
        
        function initViewLayout(obj)
        %INITVIEWLAYOUT Create a default view layout
        %  INITVIEWLAYOUT(OBJ) creates the default display.  The base
        %  implementation of this creates the first available view and displays
        %  that.
        
        hVC = obj.pCreateView;
        obj.pSetViewLayout(hVC);
        hVC.Visible = obj.Visible;
        
        end  % initViewLayout
        
        function delete(obj)
        %delete called when the view is being destroyed
        %
        
        if ~mbcgui.util.isBeingDestroyed(ancestor(obj.Parent, 'figure'))
            delete(obj.hViewContextMenu);
        end
        
        end  % delete        
        
        function sz = printSize(obj)
        %PRINTSIZE Returns the preferred printing size for the component
        %  SZ = PRINTSIZE(OBJ) returns a two element vector containing the
        %  preferred width and height for printing the component.  This method
        %  calls printSize on the selected view.
        
        if ~isempty(obj.ViewGroup.SelectedViewContainer)
            sz = obj.ViewGroup.SelectedViewContainer.printSize;
        else
            sz = printSize@mbcgui.widget.BasicContainer(obj);
            
        end
        
        end  % printSize
        
        function H = printToFigure(obj)
        %PRINTTOFIGURE Create a copy of the current view in a new figure.
        %  PRINTTOFIGURE(OBJ) creates a new figure that contains a copy of the
        %  current view.  The handle to the new figure is returned.
        
        hVC = obj.getSelectedViewContainer;
        if ~isempty(hVC)
            PR = xregGui.PointerRepository;
            PtrID = PR.stackSetPointer(obj.Parent, 'watch');
            MsgID = obj.pAddStatusMessage('Printing current view to a new figure...');
            
            hV = hVC.View;
            P = ancestor(obj.Parent, 'figure');
            
            H = figure('Color', 'w', ...
                'Visible', 'off', ...
                'Renderer', get(P, 'Renderer'), ...
                'Name', sprintf('%s - %s', hV.gettitle, obj.MessageService.getDataName), ...
                'NumberTitle', 'off');
            
            if hV.canPrint
                L = hV.printCopy(H);
            else
                hNewView = obj.ViewList.createView(obj.getSelectedViewTypeIndex, ...
                    obj.MessageService.copy, ...
                    'Parent', H);
                L = mbcgui.multiview.PanelViewContainer('Parent', H, ...
                    'View', hNewView);
            end
            if ~isprop(L,'Units') || strcmpi(get(L,'Units'),'pixels')
                figpos = get(H, 'Position');
                set(L, 'Position', [1 1 figpos(3:4)]);
                set(H, 'ResizeFcn', mbcutils.callback(@i_resizefig, H, L));
            end
            
            set(H, 'Visible', 'on');
            
            obj.pRemoveStatusMessage(MsgID);
            PR.stackRemovePointer(obj.Parent, PtrID);
        end
        end  % printToFigure
        
        function newobj = printCopy(obj, fig)
        %PRINTCOPY Create a new component for printing the component display
        %  NEWOBJ = PRINTCOPY(OBJ, FIG) creates and returns a handle to a new
        %  component, NEWOBJ, parented by the specified figure, FIG.  The new
        %  object will be used for printing a copy of the component OBJ.
        %  MultiViewPanel prints the selected view when it is printed.
        
        if ~isempty(obj.ViewGroup.SelectedViewContainer)
            newobj = obj.ViewGroup.SelectedViewContainer.printCopy(fig);
        else
            newobj = printCopy@mbcgui.widget.BasicContainer(obj,fig);
        end
        
        end  % printcopy
        
        
        function removeViews(obj,viewClass)
        %removeViews remove named multiviews
        %   removeViews(obj,viewClass)
        
        hVC = obj.getViewContainers;
        views = {hVC.View};
        viewIndices = find(cellfun(@(v) isa(v,viewClass),views));
        for i=viewIndices
            if isscalar(obj.getViewContainers)
                % can't delete last view so add another one before deleting
                % This won't work if we add a view of the same kind as we
                % are removing
                obj.splitView;
            end
            selectView(obj,hVC(i));
            deleteView(obj);
        end
        
        end
        
        function restoreViewLayout(obj, LayoutName,LytStructure)
        %RESTOREVIEWLAYOUT Restore a saved layout
        %  RESTOREVIEWLAYOUT(OBJ, LAYOUTNAME) restores a layout that has been saved
        %  in the preferences.
        
        PR = xregGui.PointerRepository;
        Pid = PR.stackSetPointer(obj.Parent, 'watch');
        
        existingLytStruct = saveViewLayout(obj, obj.ViewLayoutName);
        if nargin<3
            % restore layouts from preferences
            P = mbcprefs('mbc');
            if ispref(P, 'MultiViewPanel')
                s = getpref(P, 'MultiViewPanel');
                if isfield(s, 'Layouts')
                    s_L = s.Layouts;
                    if isfield(s_L, LayoutName)
                        ReuseContainers=hasSameViews(existingLytStruct,s_L.(LayoutName));
                        obj.pInstallLayoutStructure(s_L.(LayoutName),ReuseContainers);
                    end
                end
            end
        else
            % use structure passed in
            ReuseContainers=hasSameViews(existingLytStruct,LytStructure);
            obj.pInstallLayoutStructure(LytStructure,ReuseContainers);
        end
        PR.stackRemovePointer(obj.Parent, Pid);
        
        end  % restoreViewLayout
        
        function varargout = saveViewLayout(obj, LayoutName)
        %SAVEVIEWLAYOUT Save the current view layout
        %  SAVEVIEWLAYOUT(OBJ, LAYOUTNAME) saves a structure that defines the
        %  current set and layout of views in the preferences.  The layout can then
        %  be restored in the future by RESTOREVIEWLAYOUT.
        
        if nargin<2 || isempty(LayoutName)
            LayoutName = obj.ViewLayoutName;
        end
        
        PR = xregGui.PointerRepository;
        Pid = PR.stackSetPointer(obj.Parent, 'watch');
        
        lytStruc = obj.pGetLayoutStructure;
        if nargout==0
            % save layout to preferences
            P = mbcprefs('mbc');
            if ispref(P, 'MultiViewPanel')
                s = getpref(P, 'MultiViewPanel');
            else
                addpref(P, 'MultiViewPanel');
                s = struct('Layouts', []);
            end
            
            s.Layouts.(LayoutName) = lytStruc;
            setpref(P, 'MultiViewPanel', s);
        else
            varargout{1} = lytStruc;
        end
        
        PR.stackRemovePointer(obj.Parent, Pid);

        end  % saveViewLayout
        
        function selectView(obj, hVC)
        %SELECTVIEW Select a view
        %  SELECTVIEW(OBJ, HVIEW) selects a view.  HVIEW is a handle to either a
        %  View or a ViewContainer.
        
        if isa(hVC,'mbcgui.multiview.View')
            hAllVC = obj.ViewGroup.ViewContainers;
            hAllView = get(hAllVC, {'View'});
            hAllView = [hAllView{:}];
            hVC = obj.ViewGroup.ViewContainers(hAllView==hVC);
        end
        obj.ViewGroup.selectView(hVC);
        
        end  % selectView
        
        function splitView(obj, Orient, ViewIndex)
        %SPLITVIEW Split the currently selected view
        %  SPLITVIEW(OBJ, ORIENT, VIEWINDEX) splits the currently selected view.
        %  ORIENT specifies which direction to split the view in.  If this is left
        %  empty the view will be split in its preferred direction.  VIEWINDEX
        %  specifies the new view that should be created.  If this is left empty,
        %  the next view will be chosen automatically.
        
        hVC = obj.ViewGroup.SelectedViewContainer;
        if ~isempty(hVC)
            if nargin<3 || obj.ViewList.isViewAvailable(obj.MessageService, ViewIndex);
                P = xregGui.PointerRepository;
                PtrID = P.stackSetPointer(obj.Parent, 'watch');
                MsgID = obj.pAddStatusMessage('Splitting current view...');
                
                if nargin<3 || isempty(ViewIndex)
                    hNewVC = obj.pCreateView;
                else
                    hNewVC = obj.pCreateView(ViewIndex);
                end
                obj.ViewGroup.selectView(hNewVC);
                
                if nargin<2 || isempty(Orient)
                    Orient = hVC.View.defaultSplitDirection;
                end
                obj.pSplitView(hVC, Orient, hNewVC);
                hNewVC.Visible = obj.Visible;
                
                obj.pRemoveStatusMessage(MsgID);
                P.stackRemovePointer(obj.Parent, PtrID);
            else
                errordlg(['This view is currently not available.  ', ...
                    'Please select a different view.'], ...
                    'MBC Toolbox', 'modal');
            end
        end
        
        end  % splitView
        
        function updateAvailableViews(obj)
        %UPDATEAVAILABLEVIEWS Re-check which views can be created
        %   UPDATEAVAILABLEVIEWS(OBJ) checks whether each view type can be created
        %   and updates the state of the creation actions accordingly.
        %
        %   This method is called automatically before showing the menu that allows
        %   view creation.  If you create toolbar buttons for creating views then
        %   you should manually call this method to update the toolbar button
        %   states whenever they may have changed.
        
        AG = obj.Actions.ChangeView;
        avail = obj.ViewList.isViewAvailable(obj.MessageService);
        if length(avail)==length(AG.Actions)
            set(AG.Actions, {'Enabled'}, num2cell(avail(:)));
        end
        
        end  % updateAvailableViews

    end % public methods
    
    methods (Access=protected)
        
        function messageID = pAddStatusMessage(obj, str)
        %PADDSTATUSMESSAGE Add a message to the status bar
        %  MESSAGEID = PADDSTATUSMESSAGE(OBJ, STR) adds the string STR to the status
        %  bar if one has been provided, and returns the ID of the message added so
        %  that it can be removed later.  If no status bar has been set, an empty
        %  message id is returned.
        
        if ~isempty(obj.StatusBar) && ishandle(obj.StatusBar)
            messageID = obj.StatusBar.addMessage(str);
        else
            messageID = [];
        end
        
        end  % pAddStatusMessage
        
        function hVC = pCreateContainer(obj, varargin)
        %PCREATECONTAINER Construct a view container for a view
        %  HVC = PCREATECONTAINER(OBJ, INDEX) constructs and returns the a
        %  ViewContainer with a view.  The ViewContainer will be set up ready for
        %  insertion into the GUI.  INDEX should be the index of the view type in
        %  the ViewList.  If this is omitted or empty there will be no label on the
        %  container to identify its type.
        
        hPanel = mbcgui.container.layoutpanel('Parent', obj.Parent, ...
            'Units', 'pixels', ...
            'BorderType', 'beveledin', ...
            'Visible',obj.Visible,...
            'UIContextMenu',obj.hViewContextMenu);
        mbcgui.util.setAutoBackgroundColor(hPanel);
        
        [hV, Index] = obj.ViewList.createView(varargin{:}, ...
            obj.MessageService, ...
            'UIContextMenu',obj.hViewContextMenu,...
            'Visible',obj.Visible,...
            'Parent', hPanel);
        
        hVC = hV.createDefaultWindowContainer(hPanel);
        hVC.UIContextMenu = obj.hViewContextMenu;
        
        data.ConstructorLabel = '';
        if ~isempty(Index)
            data.ConstructorLabel = obj.ViewList.Labels{Index};
        end
        
        % Add action listeners
        data.CloseListener = event.listener(hVC, 'Close', @(h,evt) i_deleteview(h,evt, obj));
        data.SplitListener = event.listener(hVC, 'Split', @(h,evt) i_splitView(h,evt, obj));
        hVC.MVP_Data = data;
        
        obj.ViewGroup.addView(hVC);
        end  % pCreateContainer
        
        function pCreateContextMenu(obj, hMenu)
        %PADDVIEWMENUITEMS Add standard view context menu items to a menu
        %  PADDVIEWMENUITEMS(OBJ, HMENU) adds the set of menu items that appear on
        %  the standard context menu to HMENU.
        
        A = obj.Actions;
        
        A.ChangeView.createMenuItem(hMenu);
        A.ViewOptions.createMenuItem(hMenu);
        obj.GlobalViewOptionGroup.createMenuItem(hMenu);
        A.ViewActions.createMenuItem(hMenu);
        m = A.SplitToView.createMenuItem(hMenu);
        set(m(1), 'Separator', 'on');
        A.SplitViewV.createMenuItem(hMenu);
        A.SplitViewH.createMenuItem(hMenu);
        A.CloseView.createMenuItem(hMenu);
        m = A.CopyView.createMenuItem(hMenu);
        set(m, 'Separator', 'on');
        A.PrintViewFigure.createMenuItem(hMenu);
        obj.ContextActionGroup.createMenuItem(hMenu);
        
        end  % pCreateContextMenu
        
        function L = pCreateLayout(~, ViewLayout)
        %PCREATELAYOUT Create the main layout for the component
        %  L = PCREATELAYOUT(OBJ, VIEWLAYOUT) creates a alyout L that must contain
        %  the given layout VIEWLAYOUT.  VIEWLAYOUT is the layout that will contain
        %  all of the views.  The default implementation simply returns VIEWLAYOUT
        %  as the main layout.
        
        L = ViewLayout;
        
        end  % pCreateLayout
        
        function hVC = pCreateView(obj, varargin)
        %PCREATEVIEW Create a view
        %  HVC = PCREATEVIEW(OBJ, VIEWINDEX) creates the specified view.  The view
        %  will be invisible.  A handle to a viewcontainer that contains the view
        %  will be returned.
        %
        %  HVIEW = PCREATEVIEW(OBJ) chooses the view that will be created
        %  automatically.
        
        if ~isempty(obj.ViewList)
            hVC = obj.pCreateContainer(varargin{:});
        else
            error(message('mbc:mbcmultiview:MultiViewPanel:InvalidState'));
        end
        
        end  % pCreateView
        
        function AG = pCreateViewListActions(obj, ActionType)
        %PCREATEVIEWLISTACTIONS Create an ActionGroup for View creation
        %  AG = PCREATEVIEWLISTACTIONS(OBJ, ACTIONTYPE) creates and returns an
        %  action group that contains an action for creating each of the available
        %  views.  ACTIONTYPE defines the type of creation that will occur:
        %  'change' means that the actions will change the current view and 'split'
        %  means that the actions will split the current view.
        
        if nargin<2
            ActionType = 'change';
        end
        switch ActionType
            case 'change'
                objfun = @mbcgui.actions.ToggleAction;
                cb = @i_changeview;
                lbl = '&Current View';
                groupcb = @i_checkviewers;
            case 'split'
                objfun = @mbcgui.actions.StatefulAction;
                cb = @i_splitViewNum;
                lbl = '&Split View';
                groupcb = @i_checkviewers;
            otherwise
                error(message('mbc:mbcmultiview:MultiViewPanel:InvalidArgument'));
        end
        
        VL = obj.ViewList;
        A(VL.numViews)= mbcgui.actions.Action;
        for n = 1:length(A)
            A(n) = objfun({cb, obj, n}, VL.Labels{n}, VL.Descriptions{n}, VL.IconFiles{n});
        end
        AG = mbcgui.actions.ActionGroup({groupcb, obj}, lbl);
        set(AG, 'Actions', A, 'MenuType', 'submenu');
        end  % pCreateViewListActions
        
        function struc = pGetLayoutStructure(obj)
        %PGETLAYOUTSTRUCTURE Create a structure that defines the current layout
        %  STRUC = PGETLAYOUTSTRUCTURE(OBJ) creates a data structure that contains
        %  the information required to recreate the current setup of views in the
        %  object.
        
        el = get(obj.hTopLayout, 'elements');
        if ~isempty(el)
            struc = i_getviewdata(el{1});
        else
            struc.Type = 'new';
        end
        end  % pGetLayoutStructure
        
        function ret = pHasSavedLayout(obj)
        %PHASSAVEDLAYOUT Check whether a layout exists for the current layout name
        %  RET = PHASSAVEDLAYOUT(OBJ) returns true if a saved layout exists with
        %  the name specified in the ViewLayoutName property.
        
        ret = false;
        P = mbcprefs('mbc');
        if ispref(P, 'MultiViewPanel')
            s = getpref(P, 'MultiViewPanel');
            if isfield(s, 'Layouts')
                s_L = s.Layouts;
                if isfield(s_L, obj.ViewLayoutName) && isstruct(s_L.(obj.ViewLayoutName))
                    ret = true;
                end
            end
        end
        
        end  % pHasSavedLayout
        
        function pInstallLayoutStructure(obj, struc,ReuseContainers)
        %PINSTALLLAYOUTSTRUCTURE Set up the display from a saved layout
        %  PINSTALLLAYOUTSTRUCTURE(OBJ, STRUC) recreates the display described by
        %  the structure STRUC. STRUC must have been created by
        %  PGETLAYOUTSTRUCTURE.
        
        if ~isempty(struc)
            el = get(obj.hTopLayout,'elements');
            if isempty(el)
                % in construction - no containers
                ReuseContainers = false;
                el = {[]};
            end
            if ~ReuseContainers
                set(obj.hTopLayout,'Visible','off')
            end
            hL = i_createViewObject(obj, struc,ReuseContainers,el{1});
            if ~ReuseContainers
                obj.pSetViewLayout(hL);
                set(obj.hTopLayout, 'Visible', obj.Visible);
            end
        end
        end  % pInstallLayoutStructure
        
        function pRemoveStatusMessage(obj, messageID)
        %PREMOVESTATUSMESSAGE Remove a status bar message
        %  PREMOVESTATUSMESSAGE(OBJ, MESSAGEID) removes the message associated with
        %  MESSAGEID from the status bar, if one has been set,
        
        if ~isempty(obj.StatusBar) && ishandle(obj.StatusBar)
            obj.StatusBar.removeMessage(messageID);
        end
        
        end  % pRemoveStatusMessage
        
        function pSetViewLayout(obj, hDisplay)
        %PSETVIEWLAYOUT Set the layout to display
        %  PSETVIEWLAYOUT(OBJ, HDISPLAY) sets HDISPLAY as the view for the
        %  component.  HDISPLAY may be either a View object or a splitlayout.
        
        el = get(obj.hTopLayout, 'elements');
        if ~isempty(el)
            delete(el{1});
        end
        set(hDisplay, 'UserData', {obj.hTopLayout, 1});
        set(obj.hTopLayout, 'elements', {hDisplay});
        
        end  % pSetViewLayout
        
        function pSplitView(obj, hVC, Orient, hNewVC)
        %PSPLITVIEW Split a view and attach a new one
        %  PSPLITVIEW(OBJ, HVC, ORIENT, HNEWVC) splits the ViewContainer hVC in the
        %  direction specified by Orient and places the ViewContainer HNEWVC in the
        %  other half of the new splitlayout.
        
        VCdata = get(hVC, 'UserData');
        hParent = VCdata{1};
        ElIndex = VCdata{2};
        
        hLayout = xregsplitlayout(obj.Parent, ...
            'packstatus', 'off', ...
            'Visible', obj.Visible, ...
            'Left', hVC, ...
            'Right', hNewVC, ...
            'Orientation', Orient, ...
            'DividerStyle', 'flat', ...
            'DividerWidth', 4, ...
            'Userdata', VCdata);
        replace(hParent, hLayout, ElIndex);
        
        set(hVC, 'UserData', {hLayout, 1});
        set(hNewVC, 'UserData', {hLayout, 2});
        set(hParent, 'packstatus', 'on');
        
        end  % pSplitView
        
        function pUpdateActions(obj)
        %PUPDATEACTIONS Update action statuses
        %  PUPDATEACTIONS(OBJ) updates the enable status of the component's
        %  actions.
        
        % Disable actions when the component is invisible
        boolEn = strcmpi(obj.Visible, 'on');
        sA = obj.Actions;
        
        if boolEn
            if length(obj.ViewGroup.ViewContainers)>1
                sA.CloseView.Enabled = true;
            else
                sA.CloseView.Enabled = false;
            end
            
            A = [sA.SplitViewV;
                sA.SplitViewH;
                sA.ChangeView;
                sA.SplitToView];
            set(A, 'Enabled', true);
            
        else
            A = [sA.CloseView;
                sA.SplitViewV;
                sA.SplitViewH;
                sA.ChangeView;
                sA.SplitToView];
            set(A, 'Enabled', false);
            
        end
        obj.pViewSelected;
        
        end  % pUpdateActions
        
        function pViewSelected(obj)
        %PVIEWSELECTED Update object when the selected view changes
        %  PVIEWSELECTED(OBJ) is called when the selected view is changed.  This
        %  method updates the enabled status of actions and puts the view-specific
        %  actions in the correct place.
        
        A = obj.Actions;
        hVC = obj.ViewGroup.SelectedViewContainer;
        printActions = [A.CopyView; ...
            A.PrintView; ...
            A.PrintViewQuiet; ...
            A.PrintViewPreview];
        
        if ~isempty(hVC) && strcmpi(obj.Visible, 'on')
            set(printActions, 'Enabled', hVC.canPrint);
            if obj.AlwaysAllowPrintToFigure
                A.PrintViewFigure.Enabled = true;
            else
                A.PrintViewFigure.Enabled = hVC.canPrint;
            end
            hV = hVC.View;
            A.ViewOptions.Actions = hV.Options;
            A.ViewActions.Actions = hV.Actions;
        else
            set(printActions, 'Enabled', false);
            A.PrintViewFigure.Enabled = false;
            A.ViewOptions.Actions = [];
            A.ViewActions.Actions = [];
        end
        
        % Check the create action for the currently selected view
        AG = A.ChangeView;
        idx = obj.getSelectedViewTypeIndex;
        sel = false(size(AG.Actions));
        if ~isempty(idx) && idx<=length(sel) && idx>0
            sel(idx) = true;
        end
        set(AG.Actions, {'Selected'}, num2cell(sel(:)));
        
        end  % pViewSelected
        
        function setVisible(obj, val)
        %setVisible Respond to visible being set
        %  setVisible(OBJ, val) is called after the object's visible
        %  property has been set.
        
        setVisible@mbcgui.widget.BasicContainer(obj,val)
        obj.pUpdateActions;
        
        end  % pSetVisible
    end % protected methods
    
end  % classdef

function i_checkcloseable(~, evt,obj)
% When there is only one view available, make it not closeable
hVC = evt.AffectedObject.ViewContainers;
A = obj.Actions;
hasMultipleViews = (length(hVC)>1);
set(hVC, 'Closeable', hasMultipleViews);
A.CloseView.Enabled = (hasMultipleViews || strcmpi(obj.Visible, 'off'));
end  % i_checkcloseable

function i_selviewchange(~, ~, obj)
% Update Actions enabled status
obj.pViewSelected;
end  % i_selviewchange

function i_closeview(~, ~, obj)
obj.deleteView;
end  % i_closeview

function i_split(~, ~, obj, orient)
obj.splitView(orient);
end  % i_split

function i_printview(~, ~, obj, dest)
P = xregGui.PointerRepository;
ptrID = P.stackSetPointer(obj.Parent, 'watch');

msg = '';
switch dest
    case 'clipboard'
        msg = 'Copying current view to clipboard...';
    case {'printer', 'printdialog'}
        msg = 'Printing current view...';
    case 'printpreview'
        msg = 'Generating print preview for current view...';
end
if ~isempty(msg)
    MsgID = obj.pAddStatusMessage(msg);
end

obj.print(dest);

if ~isempty(msg)
    obj.pRemoveStatusMessage(MsgID);
end
P.stackRemovePointer(obj.Parent, ptrID)

end  % i_printview

function i_printtofig(~, ~, obj)
obj.printToFigure;
end  % i_printtofig

function i_deleteview(~, ~, obj)
obj.deleteView;
end  % i_deleteview

function i_splitView(~, evt, obj)
obj.splitView(evt.data.Orientation);
end  % i_splitview

function i_splitViewNum(~, ~, obj, viewnum)
obj.splitView([], viewnum);
end  % i_splitview

function i_changeview(~, ~, obj, viewnum)
obj.changeView(viewnum);
end  % i_changeview

function i_checkviewers(~, ~, obj)
obj.updateAvailableViews;
end  % i_checkviewers

function s = i_getviewdata(hL)
if isa(hL, 'xregsplitlayout')
    s = struct('Type', 'split', ...
        'Split', get(hL, 'split'), ...
        'Orientation', get(hL, 'Orientation'), ....
        'Left', i_getviewdata(get(hL, 'left')), ...
        'Right', i_getviewdata(get(hL, 'right')));
else
    hV = hL.View;
    data = hL.MVP_Data;
    s = struct('Type', 'view', ...
        'ViewLabel', data.ConstructorLabel, ...
        'ViewClass', class(hV), ...
        'ViewData', {hV.serializeView});
end
end  % i_getviewdata

function hL = i_createViewObject(obj, struc,ReuseContainers,hL)
if strcmp(struc.Type, 'split') 
    if ReuseContainers
        hLeft = get(hL,'Left');
        hRight = get(hL,'Right');
    else
        hLeft = [];
        hRight = [];
    end
    hLeft = i_createViewObject(obj, struc.Left,ReuseContainers,hLeft);
    hRight = i_createViewObject(obj, struc.Right,ReuseContainers,hRight);
    
    if ~ReuseContainers
        hL = xregsplitlayout(obj.Parent, ...
            'Visible', 'off', ...
            'Left', hLeft, ...
            'Right', hRight, ...
            'Orientation', struc.Orientation, ...
            'DividerStyle', 'flat', ...
            'DividerWidth', 4, ...
            'Split', struc.Split);
        set(hLeft, 'UserData', {hL, 1});
        set(hRight, 'UserData', {hL, 2});
    else
        
    end
else
    Index = obj.ViewList.findViewLabel(struc.ViewLabel);
    if ~ReuseContainers
        % Construct a view from the stored information
        if ~isempty(Index)
            % Construct using current view definition
            hL = obj.pCreateView(Index);
        else
            % Fall back on the view class as a constructor
            hL = obj.pCreateContainer(struc.ViewClass);
        end
    end
    % just deserialize if layouts are the same
    deserializeView(hL.View,struc.ViewData);
end
end  % i_createViewObject

function OK = hasSameViews(s1,s2)
%hasSameViews checks if the multiview has the same layout
%  ViewData is ignored in this comparison
if ~isstruct(s1) || ~isstruct(s2)
    OK = false;
elseif strcmp(s1.Type, 'split') && strcmp(s2.Type, 'split')
    % same splot and left and right the same
    OK = strcmp(s1.Orientation,s2.Orientation) && ...
        hasSameViews(s1.Left,s2.Left) && ...
        hasSameViews(s1.Right,s2.Right); 
else
    % compare Type, ViewLabel and ViewClass but not data
    OK = strcmp(s1.Type, s2.Type) && ...
        strcmp(s1.ViewLabel, s2.ViewLabel) && ...
        strcmp(s1.ViewClass, s2.ViewClass);
end

end % hasSameViews

function i_resizefig(~, ~, H, L)
figpos = get(H, 'Position');
set(L, 'Position', [1 1 figpos(3:4)]);
end  % i_resizefig