% A UIMULTICOLLIST must be created before it can be operated on
% The initial values are arg pairs as per a UICONTROL of style listbox.
%
% Calling with no arguments provides a dynamic list of the methods available:
% UIMULTICOLLIST
%
%
% h = UIMULTICOLLIST ( figHandle );
% h = UIMULTICOLLIST ( 'figHandle, units', 'normalized', 'position', [0 0 1 1] );
% If no fig handle supplier - gcf is used
% h = UIMULTICOLLIST ( 'units', 'normalized', 'position', [0 0 1 1] );
%
% After a UIMULTICOLLIST is created the following methods can be applied:
%
% Add a new column to the listbox
% UIMULTICOLLIST ( h, 'addCol', colItems, colIndex, colColour )
% UIMULTICOLLIST ( h, 'addCol', colItems, colColour, colIndex )
% colItems : { 'A' ... 'B' } (cell) | cell array of items
% optional:
% The order of these 2 optional arguments is not important.
% colIndex : nCols+1 (int) | index where column to be inserted
% colColour: 'BLACK' (char) | colour of column (HTML format colour allowed)
%
% Add a new tow to the listbox
% UIMULTICOLLIST ( h, 'addRow', rowItems , rowIndex )
% rowItems : { 'A' ... 'B' } (cell) | cell array of items
% optional:
% rolIndex : nRows+1 (int) | index where row to be inserted
%
% Change a specific item in the listbox
% UIMULTICOLLIST ( h, 'changeItem', newValue, rowIndex, colIndex )
% newValue : the new list value | char, int or double of new item in table
% rowIndex : int | row index, positive int
% colIndex : int | column index, positive int
%
% Change an entire row in the table
% UIMULTICOLLIST ( h, 'changeRow', newItems, rowIndex )
% newValue : the new list value | char, int or double of new item in table
% rowIndex : int | row index, positive int
%
% Change the colour of a specific column
% UIMULTICOLLIST ( h, 'columnColour', colIndex, 'Colour' )
% colIndex : int | column index, positive int
% colColour: 'BLACK' (char) | colour of column (HTML format colour allowed)
%
% Delete an entire column from the table
% UIMULTICOLLIST ( h, 'delCol', colIndex )
% colIndex : int | column index, int
% | 0 -> remove the last column
% | negative number is the same as end-INT
%
% Delete an entire row from the table
% UIMULTICOLLIST ( h, 'delRow', rowIndex )
% rowIndex : int | row index, positive int
% | 0 -> remove the last row
% | negative number is the same as end-INT
%
% Number of cols in the table
% UIMULTICOLLIST ( h, 'nCols' )
%
% Number of cols in the table
% UIMULTICOLLIST ( h, 'nRows' )
%
% Return the current selection in numbers (if char, NaN is returned in its place)
% UIMULTICOLLIST ( h, 'selectionInNumbers, colIndex )
% optional:
% colIndex : 1 (int) | only return the selected columns (default = all)
%
% Return the selected row(s) - but only the stated col
% UIMULTICOLLIST ( h, 'selectedStrCol', colIndex )
% colIndex : 1 (int) | only return the selected columns (default = all)
%
% Return the selected strings (use this instead of get ( h, 'string' ) as this is a HTML formatted string
% UIMULTICOLLIST ( h, 'selectedString' )
%
% Change the column separator
% UIMULTICOLLIST ( h, 'separator', newColSeparator )
% newColSeparator : ' ' (char) | replace the default ' ' as the column separator
%
% Set the 1st row to be a header - this influences the filtering below
% UIMULTICOLLIST ( h, 'setRow1Header', flag )
% flag : boolean flag (logical) | turn on/off internal flag that the 1st row is a header
%
% Interact with the string cell array
% Return the full string
% str = UIMULTICOLLIST ( h, 'string' )
% update the full string
% UIMULTICOLLIST ( h, 'string', newCellArray )
% newCellArray : square cell array | see examples
%
% Interact with the table value
% return the selected rows
% value = UIMULTICOLLIST ( h, 'value' )
% update the selected rows
% value = UIMULTICOLLIST ( h, 'value', selectedRows )
% selectedRows : int scalar/array | selected rows
%
% FILTERING
% ---------
% Special case being trialed in this version of UIMULTICOLLIST to apply filters to rows
% All of the above row interactions work on the underlying cell table.
% e.g. ADDROW, CHANGEROW, CHANGEITEM, DELROW, NROWS
% They DO NOT act on the filtered table.
% Any action on SELECTIONINNUMBERS, SELECTEDSTRCOL, SELECTEDSTR, VALUE
% will act on the filtered table data
%
% A command window method to apply filters:
% UIMULTICOLLIST ( h, 'addFilter', colIndex, filterValue )
% colIndex : 1 (int) | column to be filtered
% filterValue : filterItem(s) | cell array of filter to be applied to this col
%
% A UICONTEXTMENU can be added to allow for interactive filtering
% UIMULTICOLLIST ( h, 'applyUIFilter', useRow1AsLabels )
% optional:
% useRow1AsLabels : boolean flag | whether to use the 1st row as a flag (ignored in filtering)
% If a listbox already has a UICONTEXTMENU - the method will add a menu item to the users context menu.
%
% Remove the interactive filter
% UIMULTICOLLIST ( h, 'removeUIFilter' )
%
% Reset all the filters
% UIMULTICOLLIST ( h, 'resetFilter' )
%
% Ask the code what filters are currently applied
% output = UIMULTICOLLIST ( h, 'filtersApplied' )
%
% The user can use get and set on the handle as per normal - but it is advised
% to use the above methods for interacting with the string, as the users string
% is converted to html for formatting reasons.
%
% see also uicontrol, uicontextmenu, uimenu, get, set
%
% %% Example code:
% clear str
% d = dir;
% for i=1:length(d)
% str{i,1} = d(i).name;
% str{i,2} = d(i).date;
% str{i,3} = d(i).bytes;
% str{i,4} = d(i).isdir;
% [dummy,dummy,ext] = fileparts ( d(i).name );
% str{i,5} = strcmp ( ext, '.m' );
% end
% h=uimulticollist ( 'units', 'normalized', 'position', [0 0 1 1], 'string', str, 'columnColour', { 'RED' 'RED' 'BLUE' 'BLACK' 'GREEN' } );
%
% %% now add a header
% header = { 'FileName' 'Date' 'Bytes' 'isDir' 'isMFile' };
% uimulticollist ( h, 'addRow', header )
% %% Say we wanted to add it to the beginning - remove and add again
% uimulticollist ( h, 'delRow', 0 ); % 0 deletes the last row
% %uimulticollist ( h, 'delRow', -1 ); % -N deletes the Nth from the end
% %uimulticollist ( h, 'delRow', [1:6] ); % delete multiple rows
% uimulticollist ( h, 'addRow', header, 1 )
% %% add an extra column
% nItems = uimulticollist ( h, 'nRows' );
% uimulticollist ( h, 'addCol', [0:nItems-1] )
% %% delete the end column and add a new one at the start
% uimulticollist ( h, 'delCol', 0 ); % 0 deletes the last col
% uimulticollist ( h, 'addCol', [0:nItems-1], 1 )
% %% can also specify the colour when adding
% uimulticollist ( h, 'delCol', 1 ); % 0 deletes the last col
% uimulticollist ( h, 'addCol', [0:nItems-1], 1, 'PURPLE' )
% %% The order of the index and colour doesn't matter
% uimulticollist ( h, 'delCol', 1 ); % 0 deletes the last col
% uimulticollist ( h, 'addCol', [0:nItems-1], 'PURPLE', 1 )
% %% change an item in the list, index (row, col)
% uimulticollist ( h, 'changeItem', 'ORDER', 1, 1 )
% %% change an entire row
% uimulticollist ( h, 'changeRow', { NaN 'NAME', 'Data' 'bytes' 'isDir' 'isMFile' }, 1 )
% %% change an column col
% uimulticollist ( h, 'columnColour', 1, 'YELLOW' )
% %% retrieve selected items by column
% set ( h, 'value', 1 )
% uimulticollist ( h, 'selectedStrCol', [2] )
% set ( h, 'Max', 2, 'value', 1:3 );
% uimulticollist ( h, 'selectedStrCol', [2 3] )
% %% return the full rows of the data seleted
% uimulticollist ( h, 'selectedString' )
% %% change the default separator
% uimulticollist ( h, 'separator', ' | ' )
% %% you can get the whole string - do soem work and put it back
% str = uimulticollist ( h, 'string' );
% str{end,end} = 'XYZ';
% uimulticollist ( h, 'string', str );
% %% you can get the value & set the value
% value = uimulticollist ( h, 'value' );
% uimulticollist ( h, 'value', [2 5]);
% %% convert selection to numbers (if char -> NaN)
% data = uimulticollist ( h, 'selectionInNumbers');
% data = uimulticollist ( h, 'selectionInNumbers', 3);
% %% add a context menu to filter
% uimulticollist ( h, 'applyUIFilter');
% %% remove a filter
% uimulticollist ( h, 'removeUIFilter');
% %% user already had a context menu
% uic = uicontextmenu;
% uimenu ( uic, 'label', 'Users Items' );
% set ( h, 'uicontextmenu', uic );
% uimulticollist ( h, 'applyUIFilter', 1); % 3rd arg uses row 1 as labels
% %% reset a filter form commandline (if things go wrong)
% uimulticollist ( h, 'resetFilter');
% uimulticollist ( h, 'removeUIFilter');
% %% filtering can be applied from commandline
% uimulticollist ( h, 'setRow1Header', 1);
% uimulticollist ( h, 'addFilter', 1, [10 11]); % 3rd arg uses row 1 as labels
% %% what filtes are applied
% filters = uimulticollist ( h, 'filtersApplied' )
%
% TODO:
% -----
% A number of methods are planned for the future:
% TODO: If user passed in numbers - return numbers via the selectedStrCol method.
% TODO: Sorting lists
% TODO: If user using header - keep the haeder at the top of the list when scrolling
%
% Copyright Robert Cumming
% rcumming@matpi.com
% robertjcumming@yahoo.co.uk
% www.matpi.com
%
% The following websites were used in the development of the code
% http://www.undocumentedmatlab.com
% http://blogs.mathworks.com/loren/2010/10/14/how-many-digits-to-write/
%
%
function varargout = uimulticollist ( varargin )
newList = 1;
pairStart = 1;
parentProvided = 0;
if nargin == 0
ParseFileForCase ( which ( mfilename ) );
return
end
% is arg 1 a handle
if ishandle ( varargin{1} )
% is the handle a lisbox or a figure/dialog/uipanel
if strcmp ( get ( varargin{1}, 'type' ), 'uicontrol' ) && strcmp ( get ( varargin{1}, 'style' ), 'listbox' )
h = varargin{1};
parent = ancestor ( varargin{1}, 'figure' );
newList = 0;
else
parent = varargin{1};
pairStart = 2;
parentProvided = 1;
end
end
% is this preparting a new list for the first time
if newList
if parentProvided == 0; parent = gcf; end % if its a new list with no parent -> use gcf
if mod(length ( varargin ) - pairStart + 1 ,2) % check for pairs of inputs
error ( 'uimulticollist:IncorrectInput', 'User did not provide valid pairs on input' );
end
% has the user provided a any special pairs as input?
[varargin cString] = CheckForSpecialInputs ( 'string', varargin(pairStart:end), '' );
[varargin colours] = CheckForSpecialInputs ( 'columnColour', varargin(pairStart:end), NaN ); % 3rd input is the detault.
[varargin separator] = CheckForSpecialInputs ( 'separator', varargin(pairStart:end), ' ' );
[varargin background] = CheckForSpecialInputs ( 'separator', varargin(pairStart:end), 'white' );
% create the basic uilistbox
h = uicontrol ( 'style', 'listbox', 'parent', parent, varargin{:} );
% if user hasn't specified a background colour -> set the default to white
% if max ( userBackground ) == 0 % override the default (if the user didn't provide it)
set ( h, 'background', background );
% end
s = get ( h, 'ApplicationData' );
% make sure the strings are cells of cells
if ~iscell ( cString ) % user didn't provide, or only one item,
s.string{1,1} = cString;
else
s.string = EnsureAllStringInputCorrect ( cString );
end
% set up the other items
s.colours = colours;
s.separator = separator;
set ( h, 'ApplicationData', s )
varargout{1} = h;
else
% user interacting with an existing list
iseven = mod(length ( varargin ),2)==0;
if nargin > 1
s = get ( h, 'ApplicationData' );
[nRows,nCols]= size(s.string);
switch varargin{2}
case 'addCol' % ( h, 'addCol', colItems , ColIndex )
newData = CheckInputIsCell ( varargin{3} );
if length(newData)~=nRows
error ( 'uimulticollist:addCol', 'A new col must have an item in every row, input %i items, required %i', length(newData), nRows )
end
newColour = 'BLACK';
userSpecifedLocation=0;
if nargin >= 4
if ischar ( varargin{4} ); newColour = varargin{4}; else userSpecifedLocation = 1; insertIndex = varargin{4}; end
end
if nargin == 5
if ischar ( varargin{5} ); newColour = varargin{5}; else userSpecifedLocation = 1; insertIndex = varargin{5}; end
end
s = CheckColourVariable ( s, nCols );
if userSpecifedLocation % user specified a location
if insertIndex < 1 || insertIndex > nCols
error ( 'uimulticollist:addCol', 'user specified a column which is outwith the bounds of the data, input %i, data %i', insertIndex, nCols );
end
if insertIndex > 1
beforeIndex = s.string(:,1:insertIndex-1);
s.string = [beforeIndex newData s.string(:,insertIndex:end)];
s.colours = [s.colours(1:insertIndex-1) newColour s.colours(insertIndex:end)];
if isfield ( s, 'filters' )
s.filters = [s.filters(:,1:insertIndex-1) ones(nRows,1) s.filters(:,insertIndex:end)];
s.filterDetails = [s.filterDetails(1:insertIndex) {[]}, s.filterDetails(insertIndex:end) ];
end
else
s.string = [newData s.string];
s.colours = [newColour s.colours];
if isfield ( s, 'filters' )
s.filters = [ones(nRows,1) s.filters];
s.filterDetails = [ {[]} s.filterDetails];
end
end
else
s.string = [s.string newData];
s.colours{end+1} = newColour;
if isfield ( s, 'filters' )
s.filters(:,end+1) = 1;
s.filterDetails{end+1} = {};
end
end
s.string = EnsureAllStringInputCorrect ( s.string );
case 'addRow' % ( h, 'addRow', rowItems , rowIndwex )
newData = varargin{3};
if ~iscell ( newData )
error ( 'uimulticollist:addRow', 'A new row must be cell array of %i items', nCols )
end
if length(newData)~=nCols
error ( 'uimulticollist:addRow', 'A new row must have an item in every col, input %i items, required %i', length(newData), nCols )
end
if nargin == 4 % user specified a location
insertIndex = varargin{4};
if insertIndex > 1
beforeIndex = s.string(1:insertIndex-1,:);
s.string = [beforeIndex; newData; s.string(insertIndex:end,:)];
else
s.string = [newData; s.string];
end
else
s.string = [s.string; newData];
end
s.string = EnsureAllStringInputCorrect ( s.string );
case 'addFilter' % ( h, 'addFilter', colIndex, filterValue )
if nargin ~= 4
error ( 'uimulticollist:addFilter', 'addFilter requires 4 input arguments' );
end
if varargin{3} > nCols
error ( 'uimulticollist:addFilter', 'User provided col index, %i, which is greater than number cols, %i', varargin{3}, nCols );
end
FilterTableData ( varargin{4}, NaN, h, varargin{3} )
return
case 'applyUIFilter' % ( h, 'applyUIFilter', useRow1AsLabels )
s.uic = get ( h, 'uicontextmenu' );
if isempty ( s.uic ) % if no menu -> create one
s.uic = uicontextmenu ( 'parent', parent' );
end
names = get ( get ( s.uic, 'children' ), 'tag' );
if ~isfield ( s, 'filterMenuName' ) % check to see if it already has a filter.
s.filterMenuName = 'uimulticollist_uic';
if max ( strcmp ( names, s.filterMenuName ) ); % does the name already exist in the menu
count = 0; % loop until a unique name is found (needed for deleting later)
while max(max ( strcmp ( names, s.filterMenuName ) ))
s.filterMenuName = sprintf ( '%s%i',s.filterMenuName, count );
count = count + 1;
end
end
uimenu ( s.uic, 'label', 'Filter', 'tag', s.filterMenuName );
set ( h, 'uicontextmenu', s.uic );
s.uicRow1Labels = nargin ==3;
end
case 'changeItem' % ( h, 'changeItem, 'NewValue', rowIndex, colIndex )
if nargin ~= 5
error ( 'uimulticollist:changeItem', 'Incorrect number of input arguments - required 5' )
end
newItem = varargin{3};
row = varargin{4};
col = varargin{5};
if row < 0 || row > nRows
error ( 'uimulticollist:changeItem', 'Row index not valid, input %i items, required %i', row, length(s.string) )
end
if col < 0 || row > nCols
error ( 'uimulticollist:changeItem', 'Row index not valid, input %i items, required %i', col, length(s.string{1}) )
end
s.string{row,col} = newItem;
s.string = EnsureAllStringInputCorrect ( s.string );
case 'changeRow' % ( h, 'changeRow', 'NewItems', rowIndex )
if nargin ~= 4
error ( 'uimulticollist:changeRow', 'Change row requires 4 input arguments' );
end
rowIndex = varargin{4};
if rowIndex < 1 || rowIndex > nRows
error ( 'uimulticollist:changeRow', 'User requested to change a row < or > data in list' );
end
if length(varargin{3}) == nCols
for ii=1:nCols
s.string{rowIndex,ii} = varargin{3}{ii};
end;
else
error ( 'uimulticollist:changeRow', 'New row does not contain the correct number of items, input %i, required %i', length(varargin{2}), nCols );
end
s.string = EnsureAllStringInputCorrect ( s.string );
case 'columnColour' % ( h, 'columnColor', colIndex, 'Colour' )
if nargin ~= 4
error ( 'uimulticollist:columnColour', 'Change colour requires 4 input arguments' );
end
s = CheckColourVariable ( s, nCols );
s.colours{varargin{3}} = varargin{4};
case 'delCol' % ( h, 'delCol', colIndex )
if nargin ~= 3
error ( 'uimulticollist:delCol', 'Delete column requires 3 input arguments' );
end
if varargin{3} <= 0
s.string(:,end+varargin{3}) = [];
s.colours(end+varargin{3}) = [];
if isfield ( s, 'filters' );
end
else
s.string(:,varargin{3}) = [];
s.colours(varargin{3}) = [];
end
case 'delRow' % ( h, 'delRow', rowIndex )
if nargin ~= 3
error ( 'uimulticollist:delRow', 'Delete row requires 3 input arguments' );
end
if varargin{3} <= 0
s.string(end+varargin{3},:) = [];
else
s.string(varargin{3},:) = [];
end
case 'filtersApplied' % ( h, 'filtersApplied )
varargout{1} = s.filterDetails;
return
case 'nCols' % ( h, 'nCols' )
varargout{1} = nCols;
case 'nRows' % ( h, 'nRows' )
varargout{1} = nRows;
case 'removeUIFilter' % ( h, 'removeUIFilter' )
fMenu = get ( s.uic, 'children' );
fMenuIndex = strcmp ( get ( fMenu, 'tag' ), s.filterMenuName );
delete ( get ( fMenu(fMenuIndex), 'children' ));
delete ( fMenu(fMenuIndex) );
if length(fMenu) == 1
delete ( s.uic );
end
s = rmfield ( s, { 'uic' 'filterMenuName' 'uicRow1Labels'} );
case 'resetFilter' % ( h, 'resetFilter' )
if isfield ( s, 'filters' ); s = rmfield ( s, 'filters' ); end
if isfield ( s, 'filterDetails' ) ; s = rmfield ( s, 'filterDetails' ); end
case 'selectionInNumbers' % ( h, 'seletionInNumbers', colIndex )
val = get ( h, 'value' );
if nargin==2
varargout{1} = cellfun ( @str2double, s.string(val,:));
else
varargout{1} = cellfun ( @str2double, s.string(val,varargin{3}));
end
case 'selectedStrCol' % ( h, 'selectedStrCol', colIndex )
if nargin ~= 3
error ( 'uimulticollist:selectedStrCol', 'selectedStrCol requires 3 input arguments' );
end
val = get ( h, 'value' );
colIndex = varargin{3};
if length(val) > 1
for jj=1:length(val)
for kk=1:length(colIndex)
varargout{1}{jj,kk} = s.string{val(jj),colIndex(kk)};
end
end
else
% varargout{1} = {s.string{val,varargin{3}}};
varargout{1} = s.string(val,varargin{3});
end
case 'selectedString' % ( h, 'selectedString' )
val = get ( h, 'value' );
if length(val) > 1
varargout{1} = s.string(val,:);
else
% varargout{1} = {s.string{val,:}};
varargout{1} = s.string(val,:);
end
case 'separator' % ( h, 'separator', 'newColSeparator' )
if nargin ~= 3
error ( 'uimulticollist:separator', 'separator requires 3 input arguments' );
end
s.separator = varargin{3};
case 'setRow1Header' % ( h, 'setRow1Header', OnOffFlag )
if nargin ~= 3
error ( 'uimulticollist:setRow1Header', 'setRow1Header requires 3 input arguments' );
end
s.uicRow1Labels = varargin{3};
case 'string' % ( h, 'string', cellArrayStrings )
if iseven
varargout{1} = s.string;
else
s.string = varargin{3};
end
case 'value' % ( h, 'value', selectionValue )
if iseven
varargout{1} = get ( h, 'value' );
else
set ( h, 'value', varargin{3} );
end
otherwise
text = ParseFileForCase ( which ( mfilename ) );
error ( 'uimulticollist:Unsupported', 'Unsupported request "%s"\n Suitable methods are:\n%s', varargin{2}, text )
end
set ( h, 'ApplicationData', s );
else
error ( 'uimulticollist:InputError', 'User must provide extra inputs to interact with a live listbox' );
end
end
% has filters been applied?
if isfield ( s, 'filters' );
s = get ( h, 'ApplicationData' );
s.filters = ones(nRows,nCols);
for i=1:length(s.filterDetails)
for j=1:length(s.filterDetails{i})
s.filters(~strcmp ( s.string(:,i), s.filterDetails{i}{j} ),i) = 0;
end
end
set ( h, 'ApplicationData', s );
end
% Update the html string in the listbox
UpdateStringHTML(h);
% if a uicontextmenu -> update it.
if isfield ( s, 'uic' )
UpdateFilterMenu ( h );
end
end
%% Update the filter menu
function UpdateFilterMenu ( h )
s = get ( h, 'ApplicationData' );
[nRows,nCols]= size(s.string);
if ~isfield ( s, 'uic' ); return; end
fMenu = get ( s.uic, 'children' );
fMenuIndex = strcmp ( get ( fMenu, 'tag' ), s.filterMenuName );
delete ( get ( fMenu(fMenuIndex), 'children' ));
for i=1:nCols
if s.uicRow1Labels
label = s.string{1,i};
else
label = sprintf ( 'Col %i', i );
end
uimenu ( fMenu(fMenuIndex), 'label', label, 'Callback', {@FilterTableData h i} );
end
if isfield ( s, 'filters' )
uimenu ( fMenu(fMenuIndex), 'label', 'Reset Filters', 'Callback', {@FilterTableData h 0}, 'separator', 'on' );
end
end
%% Filter the string data
function FilterTableData ( obj, eventdata, h, colIndex )
s = get ( h, 'ApplicationData' );
[nRows,nCols]= size(s.string);
if colIndex == 0 % removing the filters
s = rmfield ( s, { 'filters' 'filterDetails' } );
else
if ~isfield ( s, 'filters' )
s.filters = ones(nRows,nCols);
end
if ~isfield ( s, 'uicRow1Labels' ); s.uicRow1Labels = 0; end
if s.uicRow1Labels
items = s.string(2:end,colIndex);
else
items = s.string(:,colIndex);
end
items = ['None'; unique(items)];
try
[dummy,initValue]=intersect ( items, s.filterDetails{colIndex} ); %#ok<*ASGLU>
catch %#ok<CTCH> Ok to crash - if no matches found or no filter applied.
initValue = 1;
end
if ~isnan ( eventdata )
[a,b] = listdlg('PromptString','Filter Items','SelectionMode','multi','ListString',items, 'initialValue', initValue);
if b == 0; return; end
else
obj = CheckInputIsCell ( obj );
filterValue= EnsureAllStringInputCorrect (obj);
[dummy,a] = intersect ( items, filterValue );
end
if length(a)==1 && a == 1 % resetting the filter.
s.filterDetails{colIndex} = [];
s.filters(:,colIndex) = 1;
else
s.filterDetails{colIndex} = items(a);
s.filters(:,colIndex) = 0;
for ii=1:length(a)
s.filters(strcmp ( s.string(:,colIndex), items(a(ii)) ),colIndex) = 1;
end
% s.filters(:,colIndex)=s.filters(:,colIndex)==2;
end
if s.uicRow1Labels
s.filters(1,colIndex) = 1;
end
% check the filtering hasn't make the listbox disappear
val = get ( h, 'value' );
val(val > length(find(sum(s.filters,2)==6))) = [];
if isempty ( val );
val = 1;
end
set ( h, 'value', val );
end
set ( h, 'ApplicationData', s );
UpdateStringHTML( h );
UpdateFilterMenu ( h );
end
%% Update the text to HTML format.
function UpdateStringHTML (h)
s = get ( h, 'ApplicationData' );
str = s.string;
sz=size(str);
nRow = sz(1);
% work out the width of each column
nCol = sz(2);
colWidths = zeros(nCol,1);
colDefined = iscell( s.colours );
if colDefined
if length(s.colours) ~= nCol
error ( 'uimulticollist:IncorrectInput', 'length of column colours (%i) does not match the number of columns (%i)', length(s.colours), nCol )
end
end
for ii=1:nCol
for jj=1:nRow
colWidths(ii) = max ( colWidths(ii), length(str{jj,ii}) );
end
end
%
% if s.headedList
% s.header = ConvertStringToHTML ( s.header, ' ', colWidths );
% end
details = cell(nRow,1);
for i=1:nRow
details{i} = '<HTML>';
if colDefined
details{i} = ConvertStringToHTML ( str(i,:), s.separator, colWidths, s.colours );
else
details{i} = ConvertStringToHTML ( str(i,:), s.separator, colWidths );
end
end
if isfield ( s, 'filters' )
details(~logical(sum(s.filters,2)==nCol)) = [];
% TODO: If filter wipes out seleted values?
end
s.listboxString = details;
set ( h, 'string', details, 'fontname', 'monospaced', 'ApplicationData', s );
end
%% HTML string conversion
function output = ConvertStringToHTML ( str, sep, colWidths, colours )
nCol = length(str);
colDefined = nargin == 4;
output = '<HTML>';
for j=1:nCol
text = str{j};
text = LeftFormatString(text,colWidths(j));
if colDefined
text = sprintf ( '<FONT color="%s">%s</FONT>', colours{j}, text );
end
if j==1
output = sprintf ( '%s%s', output, text );
else
output = sprintf ( '%s%s%s', output, sep, text );
end
end
output = sprintf ( '%s</HTML>', output );
end
%% format the string so its left formatted
function output = LeftFormatString(input, nLen)
formatString = sprintf ( '%%%is', nLen );
output = fliplr(sprintf ( formatString, fliplr(input) ));
spaces = length(input)+1;
output = sprintf ( '%s%s', output(1:spaces-1), regexprep ( output(spaces:end), ' ', ' ' ) );
end
%% convert the string to a double.
function str = double2CompactStr ( inputStr )
if iscell ( inputStr )
inputStr = cell2mat( inputStr );
end
str = '';
for i=1:length(inputStr)
t_str = sprintf ( '%f', inputStr(i) );
n = regexp(t_str,'\.0*$');
if ~isempty(n)
t_str(n:end) = [];
else
m = regexp(t_str,'0*$');
if ~isempty(m)
t_str(m:end) = [];
end
end
str = sprintf ( '%s %s', str, t_str );
end
str = strtrim ( str );
end
%% check the input strings.
function cString = EnsureAllStringInputCorrect ( cString )
% nCols = length(cString{1});
% check all sub items are cell arrays
% for ii=1:length(cString)
% if ~iscell ( cString{ii} )
% error ( 'uimulticollist:IncorrectInput', 'Each row needs to be a cell array' )
% end
% end
% check the input data is correct
% for ii=1:length(cString)
% if length(cString{ii})~=nCols
% error ( 'uimulticollist:IncorrectInput', 'All Input data does not contain the same number of rows' )
% end
% end
% check all items in the cells are strings
s = size(cString);
for ii=1:s(1)%length(cString)
for jj=1:s(2)%length(cString{ii})
if isnumeric ( cString{ii,jj} )
cString{ii,jj} = double2CompactStr ( cString{ii,jj} );
elseif ischar ( cString{ii,jj} )
elseif islogical ( cString{ii,jj} )
cString{ii,jj} = double2CompactStr ( cString{ii,jj} );
else
error ( 'uimulticollist:IncorrectInput', 'Each item in a row can be numeric or char only' )
end
end
end
end
%% check the input for any special pairs
function [output data] = CheckForSpecialInputs ( name, output, data )
colIndex = strcmp ( output(1:2:end), name );
if max ( colIndex ) == 1
vIndex = 2*find(colIndex==1);
data = output{(vIndex)};
output(vIndex-1) = [];
output(vIndex-1) = []; % remove it from the varargin passed to the uicontrol
end
end
%% parse file for help
function text = ParseFileForCase ( filename )
fid = fopen ( filename, 'r' );
text = '';
if fid ~= -1
while ( true )
line = fgetl ( fid );
if line == -1; break; end
if length(line)>4
if ~isempty ( strfind ( line, sprintf ( 'ca%s', 'se' ) ) )
trimLine = strtrim ( line );
if ~strcmp ( trimLine(1), '%' )
if nargout == 0
disp ( line );
else
text = sprintf ( '%s\n%s', text, line );
end
end
end
end
end
end
end
%% check the input is a cell
function newData = CheckInputIsCell ( newData )
if ~iscell(newData)
for jj=1:length(newData)
temp{jj,1} = newData(jj); %#ok<AGROW>
end
newData = temp;
end
end
function s = CheckColourVariable ( s, nCols )
if ~iscell ( s.colours ) % if it isn't set - make them all black;
s = rmfield ( s, 'colours' );
for jj=1:nCols;
s.colours{jj} = 'BLACK';
end
end
end
%% %% some initial coding for the headed listbox....
% s.headedList = headedList;
% s.lastIndex = 1;
% if ~iscell ( header )
% s.header = {header};
% else
% s.header = header;
% end
% if s.headedList
%
% if exist ( 'findjobj', 'file' ) ~= 2
% error ( 'uimulticollist:headedList', 'Required function findjobj not found - can be downloaded from matlab FEX' );
% end
% jScrollPane = findjobj(h,'propert',{'tag',tag});
% if length ( jScrollPane ) ~= 1
% error ( 'uimulticollist:findjobj', 'findjobj found to many objects to work on' );
% end
% if isempty ( jScrollPane ) % this happens if the figure is invisible.
% figHandle = ancestor ( parent, 'figure' );
% s = get ( 0, 'screensize' );
% p = get ( figHandle, 'position' );
% set ( figHandle, 'position', [0-p(3) 0-s(4), 0.5*p(3), 0.5*p(4)], 'visible', 'on' );
% jScrollPane = findjobj(tmp);
% set ( figHandle, 'visible', 'off', 'position', p ); % put it back and turn it off again.
% end
% jScroll=handle ( jScrollPane, 'CallbackProperties' );
% set ( jScroll, 'AdjustmentValueChangedCallback', {@UpdateHeadedListCallback h} );
% end