Code covered by the BSD License  

Highlights from
uiListFields

image thumbnail
from uiListFields by Christophe Tilman
UILISTFIELDS creates a list uicontrol component to visualize data structure.

uiListFields
classdef uiListFields < handle
% UILISTFIELDS creates a list uicontrol component with hierarchical data in a figure window.
%   UILISTFIELDS creates an empty uiListFields object with default property values in
%   a figure window.
%
%   UILISTFIELDS('PropertyName1', 'Value1', 'PropertyName2', 'Value2', ...)
%   creates a uiListFields object with the specified properties. The properties
%   that can be set are the same as for list uicontrol plus 'Content'.
%   The 'Content' property must be an object with fields (like a structure).
%
%   UILISTFIELDS(figurehandle, ...) creates a uiListFields object in the figure
%   window specified by the figurehandle.
%
%   HANDLE = UILISTFIELDS(...) creates a uiListFields object and returns handle which correspond to
%   the list uicontrol object handle.
%
%   Properties:
%
%       Content - structure that will be displayed in the uiListFields object
%       FullStructure - fields list of Content including sub levels
%       Levels - numbers representing hierarchical level of fullstructure
%       IsNode - 1 if is node
%       IsVisible - 1 if visible in the list
%       SelectedContent - give the value of the selected line of the displayed structure
%
%       on top of these properties, all uicontrol properties are present.
%
%
%   Examples:
%           MyStruct.field1='hello';
%           MyStruct.field2.subfield1=123;
%           MyStruct.field2.subfield2=ones(5,4);
%           lf = uiListFields(gcf,'Position',[20 20 200 300],'Content',MyStruct)
%
%       %Creates a uiListFields in current figure window and display structure of MyStruct.
%       %If a line is selected in the list, use up, down, left and rigth
%       %arrow to navigate into the structure.
%       %If Content is a handle, properties are displayed.
%
%           get(lf,'SelectedContent')
%
%       %Return content of selected item of 'MyStruct'
%
%           display(lf)
%
%       %Return names, levels, isnode and description of MyStruct in a
%       structured variable.
%

%   Author: Christophe Tilman
%   Creation date: 14/08/09
%   Contact, comments, suggestions: chr_tilman@hotmail.com

properties (Hidden=true)
    hlistbox%handle of list control object
    content%structure
    fullstructure%explored structure
    levels%list of levelsof fullstructure
    description%list of description of fullstructure
    isnode%list of logical values for each element of fullstructure
    isvisible%list of logical values for each element of fullstructure
end
properties (SetObservable=true,Hidden=true)
    isdisplayed%list of logical values for each element of fullstructure
end

methods
    function obj=uiListFields(varargin)
        addlistener(obj,'isdisplayed','PostSet',@(src,evnt)handlePropertyEvents(obj,src,evnt));
        obj.hlistbox=uicontrol;
        if nargin/2~=round(nargin/2)
            set(obj.hlistbox,'parent',varargin{1})
            i=2:nargin;
        else
            i=1:nargin;
        end
        if ~isempty(i)
            obj.set(varargin{i})
        end
        %force the uicontrol to be listbox and use courier fontname
        set(obj.hlistbox,'style','listbox',...
            'FontName','courier',...
            'KeyPressFcn',@(src,evnt)listboxcallback(obj,src,evnt))
    end
    function set(obj,varargin)
        flds=fieldnames(get(obj.hlistbox));
        for i=1:2:nargin-1;
            if strmatch(lower(varargin{i}),lower(flds),'exact')
                switch lower(varargin{i})
                    case 'style'
                        %no action as the style have to be "listbox"
                    otherwise
                        set(obj.hlistbox,varargin{i},varargin{i+1})
                end
            else
                switch lower(varargin{i})
                    case 'content'
                        if ishandle(varargin{i+1})
                            varargin{i+1}=get(varargin{i+1});
                        end
                        if hasfields(varargin{i+1})
                            obj.content=varargin{i+1};
                            %build structure
                            [obj.fullstructure,obj.levels,obj.description]=deploystruct(obj.content,0);
                            obj.isnode=[(diff(obj.levels)==1) 0];
                            obj.isdisplayed=(obj.levels==1);
                        end
                end
            end
        end
    end
    function out=display(obj)
        out.name=obj.fullstructure';
        out.level=obj.levels';
        out.isnode=obj.isnode';
        out.description=obj.description';
    end
    function out=get(obj,varargin)
        %get listbox properties
        out=get(obj.hlistbox);
        %add uiListFields properties
        out.Content=obj.content;
        out.FullStructure=obj.fullstructure;
        out.Levels=obj.levels;
        out.IsNode=obj.isnode;
        out.IsVisible=obj.isvisible;
        %get SelectedContent property
        if isempty(obj.content)
            out.SelectedContent=[];
        else
            value=get(obj.hlistbox,'value');
            originalvalue=find(cumsum(obj.isvisible)==value);
            originalvalue=originalvalue(1);
            str='out.SelectedContent=out.Content';
            for i=1:obj.levels(originalvalue)
                index=find(obj.levels(1:originalvalue)==i);
                str=[str '.' obj.fullstructure{index(end)}];
                try
                    eval([str ';'])
                catch
                    break
                end
            end
        end
        %allow only one input argument
        %if no argument, give all of them
        if length(varargin)==1 
            if strmatch(varargin{1},fieldnames(out),'exact')
                out=out.(varargin{1});
            else
                error(['There is no ''' varargin{1} ''' property in ''uiListFields'' class.']);
                out=[];
            end
        elseif length(varargin)>1 
            out=[];
            error('Too many input arguments.');
        end
    end
    function handlePropertyEvents(obj,src,event)
        switch src.Name
            case 'isdisplayed'
                %build the string structure representing the structure of
                %Content
                str=[];
                nonvisiblelevel=[];
                obj.isvisible=obj.isdisplayed;
                for i=1:length(obj.fullstructure)
                    if ~isempty(nonvisiblelevel) && nonvisiblelevel>=obj.levels(i)
                        nonvisiblelevel=[];
                    end
                    if obj.isdisplayed(i) && isempty(nonvisiblelevel)
                        if obj.isnode(i)
                            if obj.isdisplayed(i+1)
                                str{end+1}=[32*ones(1,2*obj.levels(i)-2) '- ' obj.fullstructure{i}...
                                    ' (' obj.description{i} ')'];
                            else
                                str{end+1}=[32*ones(1,2*obj.levels(i)-2) '+ ' obj.fullstructure{i}...
                                    ' (' obj.description{i} ')'];
                            end
                        else
                            str{end+1}=[32*ones(1,2*obj.levels(i)-2) '  ' obj.fullstructure{i}...
                                ' (' obj.description{i} ')'];
                        end
                    end
                    if obj.isdisplayed(i) && ~isempty(nonvisiblelevel)
                        obj.isvisible(i)=0;
                    end
                    if ~obj.isdisplayed(i) && isempty(nonvisiblelevel)
                        nonvisiblelevel=obj.levels(i)-1;
                    end
                end
                set(obj.hlistbox,'string',str)
                if get(obj.hlistbox,'value')>length(str)
                    set(obj.hlistbox,'value',length(str))
                end
        end
    end
    function listboxcallback(obj,src,event)
        %management of arrow keys
        %up and down have no effect
        %right deploy the structure if is node
        %left contract the structure if is node or goes to upper level if
        %already contracted
        value=get(src,'value');
        originalvalue=find(cumsum(obj.isvisible)==value);
        originalvalue=originalvalue(1);     
        visible=obj.isdisplayed;
        if obj.isnode(originalvalue)&& ((obj.isdisplayed(originalvalue+1) && strcmp(event.Key,'leftarrow')) || ...
                    (~obj.isdisplayed(originalvalue+1) && strcmp(event.Key,'rightarrow')))
                for i=originalvalue+1:length(obj.levels)
                    if obj.levels(i)==obj.levels(originalvalue)
                        break
                    elseif obj.levels(i)==obj.levels(originalvalue)+1
                        visible(i)=(visible(i)==0);
                    end
                end
                obj.isdisplayed=visible;
        elseif strcmp(event.Key,'leftarrow')
            index=find(obj.isvisible(1:originalvalue-1) & obj.levels(1:originalvalue-1)<obj.levels(originalvalue));
            if ~isempty(index);set(obj.hlistbox,'value',sum(obj.isvisible(1:index(end))));end
        end        
    end
    function delete(obj)
        delete(obj.hlistbox)
    end
end
end

function [out,sublevel,description]=deploystruct(in,level)
%explore the structure of in and return structure list, level list and
%description list
    flds=fieldnames(in);
    out=[];
    sublevel=[];
    description=[];
    for i=1:length(flds)
        out{end+1}=flds{i};
        sublevel(end+1)=level+1;
        description{end+1}=class(in(1).(flds{i}));
        if hasfields(in(1).(flds{i}))
            [substruct,subsublevel,subdescription]=deploystruct(in(1).(flds{i}),level+1);
            for j=1:length(substruct)
                out{end+1}=substruct{j};
                sublevel(end+1)=subsublevel(j);
                description{end+1}=subdescription{j};
            end
        end
    end
end

function out=hasfields(in)
%check that in has accessible fields
try
    fieldnames(in);
    out=1;
catch
    out=0;
end
end

Contact us at files@mathworks.com