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