function [t,sts] = cfg_getfile(varargin) % File selector % FORMAT [t,sts] = cfg_getfile(n,typ,mesg,sel,wd,filt,prms) % n - Number of files % A single value or a range. e.g. % 1 - Select one file % Inf - Select any number of files % [1 Inf] - Select 1 to Inf files % [0 1] - select 0 or 1 files % [10 12] - select from 10 to 12 files % typ - file type % 'any' - all files % 'batch' - matlabbatch batch files (.m, .mat and XML) % 'dir' - select a directory. By default, hidden ('.xyz') and % MATLAB class directories are not shown. % 'mat' - Matlab .mat files or .txt files (assumed to contain % ASCII representation of a 2D-numeric array) % 'xml' - XML files % Other strings act as a filter to regexp. This means % that e.g. DCM*.mat files should have a typ of '^DCM.*\.mat$'. % A combination of types can be specified as a cellstr list of % types. A file must match at least one of the specified types. % mesg - a prompt (default 'Select files...') % sel - list of already selected files % wd - Directory to start off in % filt - value for user-editable filter (default '.*'). Can be a % string or cellstr. If it is a cellstr, filter expressions % will be concatenated by '|' (regexp OR operator). % prms - Type specific parameters % % t - selected files % sts - status (1 means OK, 0 means window quit) % % FORMAT [t,ind] = cfg_getfile('Filter',files,typ,filt,prms,...) % filter the list of files (column cell array) in the same way as the % GUI would do. The 'prms...' argument(s) will be passed to a typ % specific filtering function, if available. % When filtering directory names, the filt argument will be applied to the % last directory in a path only. % t returns the filtered list (cell array), ind an index array, such that % t = files(ind). % % FORMAT cpath = cfg_getfile('CPath',path,cwd) % function to canonicalise paths: Prepends cwd to relative paths, processes % '..' & '.' directories embedded in path. % path - cellstr containing path names % cwd - cellstr containing current working directory [default '.'] % cpath - conditioned paths, in same format as input path argument % % FORMAT [files,dirs]=cfg_getfile('List'[,direc[,typ[,filt[,prms]]]]) % Returns files matching the filter (filt) and directories within direc % direc - directory to search. Defaults to pwd. % typ - file type % filt - additional filter to select files with (see regexp) % e.g. '^w.*\.img$' % files - files matching 'typ' and 'filt' in directory 'direc' % dirs - subdirectories of 'direc' % FORMAT [files,dirs]=cfg_getfile('FPList'[,direc[,typ[,filt[,prms]]]]) % As above, but returns files with full paths (i.e. prefixes direc to % each). % FORMAT [files,dirs]=cfg_getfile('FPListRec'[,direc[,typ[,filt[,prms]]]]) % As above, but returns files with full paths (i.e. prefixes direc to % each) and searches through sub directories recursively. % % FORMAT cfg_getfile('PrevDirs',dir) % Add directory dir to list of previous directories. % FORMAT dirs=cfg_getfile('prevdirs') % Retrieve list of previous directories. % % FORMAT cfg_getfile('DirFilters', filter_list) % Specify a list of regular expressions to filter directory names. To show % all directories, use {'.*'}. Default is {'^[^.@]'}, i.e. directory names % starting with '.' or '@' will not be shown. % % FORMAT cfg_getfile('ListDrives'[, reread]) % On PCWIN(64) machines, list all available drive letters. If reread is % true, refresh internally cached list of drive letters. % % This code is based on the file selection dialog in SPM5, with virtual % file handling turned off. %____________________________________________________________________________ % Copyright (C) 2005 Wellcome Department of Imaging Neuroscience % % This code is part of a batch job configuration system for MATLAB. See % help matlabbatch % for a general overview. %_______________________________________________________________________ % Copyright (C) 2007 Freiburg Brain Imaging % John Ashburner and Volkmar Glauche % $Id: cfg_getfile.m 7067 2017-04-20 16:49:53Z guillaume $ t = {}; sts = false; if nargin > 0 && ischar(varargin{1}) switch lower(varargin{1}) case 'cpath' if nargin >= 2 && nargin <= 3 if all(cellfun(@iscellstr,varargin(2:end))) t = cpath(varargin{2:end}); sts = true; else cfg_message('cfg_getfile:notcellstr','Inputs to %s(''%s'',...) must be cellstr.', mfilename, varargin{1}); end else cfg_message('cfg_getfile:nargin','%s(''%s'',...) Wrong number of inputs.', mfilename, varargin{1}); end case 'filter' t = varargin{2}; if isempty(t) || (numel(t) == 1 && isempty(t{1})) sts = 1; return; end if ndims(t) > 2 || size(t,2) > 1 cfg_message('cfg_getfile:notcolumncell','Input file lists to %s(''%s'',...) must be a column cellstr.', mfilename, varargin{1}); end t = cpath(t); [unused,n,e] = cellfun(@fileparts,t,'UniformOutput',false); t1 = strcat(n,e); filt = mk_filter(varargin{3:end}); [unused,sts] = do_filter_exp(t1,filt); t = t(sts); case {'list', 'fplist', 'fplistrec'} if nargin < 2 direc = pwd; else direc = varargin{2}; end if nargin < 3 typ = 'any'; else typ = varargin{3}; end if nargin < 4 filt = '.*'; else filt = varargin{4}; end if nargin < 5 prms = {}; else prms = varargin(5:end); end filt = mk_filter(typ, filt, prms{:}); direc = cpath(cellstr(direc)); t = cell(numel(direc),1); sts = cell(numel(direc),1); for k = 1:numel(t) if regexpi(varargin{1},'rec') [t{k}, sts{k}] = select_rec1(direc{k}, filt, false); else [t{k}, sts{k}] = listfiles(direc{k}, filt, false); % (sts is subdirs here) sts{k} = sts{k}(~(strcmp(sts{k},'.')|strcmp(sts{k},'..'))); % remove '.' and '..' entries if regexpi(varargin{1}, 'fplist') % return full pathnames if ~isempty(t{k}) % starting with r2012b, this could be written as % t{k} = fullfile(direc{k}, t{k}); t{k} = strcat(direc{k}, filesep, t{k}); end if (nargout > 1) && ~isempty(sts{k}) sts{k} = cpath(sts{k}, direc(k)); end end end end t = cat(1,t{:}); sts = cat(1,sts{:}); case 'prevdirs' if nargin > 1 prevdirs(varargin{2}); end if nargout > 0 || nargin == 1 t = prevdirs; sts = true; end case 'regfilter' t = reg_filter(varargin{2:end}); case 'dirfilters' df = reg_filter('dir'); df.regex = varargin{2}; df = struct2cell(df); reg_filter(df{:}); case 'listdrives' if ispc t = listdrives(varargin{2:end}); else t = {}; end otherwise cfg_message('matlabbatch:usage','Inappropriate usage.'); end else [t,sts] = selector(varargin{:}); end %======================================================================= %======================================================================= function [t,ok] = selector(n,typ,mesg,already,wd,filt,varargin) % Limits if nargin<1 || ~isnumeric(n) || numel(n) > 2 n = [0 Inf]; else n(~isfinite(n)) = Inf; if numel(n)==1, n = [n n]; end if n(1)>n(2), n = n([2 1]); end if ~isfinite(n(1)), n(1) = 0; end end % Filter if nargin<2 || ~(ischar(typ) || iscellstr(typ)), typ = 'any'; end if nargin<6 || ~(ischar(filt) || iscellstr(filt)), filt = '.*'; end if iscellstr(filt) && numel(filt) > 1 filt_or = sprintf('(%s)|',filt{:}); filt_or = filt_or(1:end-1); else filt_or = char(filt); end ok = 0; t = ''; sfilt = mk_filter(typ,filt,varargin{:}); % Message if nargin<3 || ~(ischar(mesg) || iscellstr(mesg)) mesg = 'Select files...'; elseif iscellstr(mesg) mesg = char(mesg); end % Already selected files if nargin<4 || isempty(already) || (iscell(already) && isempty(already{1})) already = {}; else % Canonicalise already selected paths already = cpath(already); % Filter already selected files by type, but not by user defined filter [pd, nam, ext] = cellfun(@(a1)fileparts(a1), already, ... 'UniformOutput',false); sfilt1 = sfilt; sfilt1.filt = '.*'; [unused, ind] = do_filter_exp(strcat(nam,ext),sfilt1); already = already(ind); % Add folders of already selected files to prevdirs list prevdirs(pd(ind)); end % Working directory if nargin<5 || isempty(wd) || ~(iscellstr(wd) || ischar(wd)) if isempty(already) wd = pwd; else wd = fileparts(already{1}); if isempty(wd) wd = pwd; end end end wd = char(cpath(cellstr(wd))); [col1,col2,col3,lf,bf] = colours; % delete old selector, if any fg = findobj(0,'-depth',1,'Tag',mfilename); if ~isempty(fg) delete(fg); end % create figure fg = figure('IntegerHandle','off',... 'Tag',mfilename,... 'Name',mesg,... 'NumberTitle','off',... 'Units','Pixels',... 'MenuBar','none',... 'DefaultTextInterpreter','none',... 'DefaultUicontrolInterruptible','on',... 'Visible','off'); cfg_onscreen(fg); set(fg,'Visible','on'); sellines = min([max([n(2) numel(already)]), 4]); filtlines = sum(arrayfun(@(f)numel(f.prms.val), sfilt.tfilt)); [pselp, pfiltp, pcntp, pfdp, pdirp] = panelpositions(fg, sellines+1, filtlines); % Messages are displayed as title of Selected Files uipanel psel = uipanel(fg,... 'units','normalized',... 'Position',pselp,... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'BorderType','none',... 'Tag','msg'); % Selected Files sel = uicontrol(psel,... 'style','listbox',... 'units','normalized',... 'Position',[0 0 1 1],... lf{:},... 'Callback',@unselect,... 'tag','selected',... 'BackgroundColor',col1,... 'ForegroundColor',col3,... 'Max',10000,... 'Min',0,... 'String',already,... 'Value',1); c0 = uicontextmenu('Parent',fg); set(sel,'uicontextmenu',c0); uimenu('Label','Unselect All', 'Parent',c0,'Callback',@unselect_all); % Filters uipanel if filtlines > 0 pfilt = uipanel(fg,... 'units','normalized',... 'Position',pfiltp,... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'BorderType','none',... 'Tag','pfilt'); filth = 1/filtlines; cfiltp = 0; for cf = 1:numel(sfilt.tfilt) [id, stop, vals] = list(sfilt.tfilt(cf).prms,cfg_findspec({{'class','cfg_entry'}}),cfg_tropts(cfg_findspec,0,inf,0,inf,false),{'name','val'}); for ci = 1:numel(id) % Label uicontrol(pfilt,... 'style','text',... 'units','normalized',... 'Position',[0.02 cfiltp 0.47 filth],... 'HorizontalAlignment','left',... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'String',vals{1}{ci}); % Value uid.cf = cf; uid.id = id{ci}; uicontrol(pfilt,... 'style','edit',... 'units','normalized',... 'Position',[0.51 cfiltp 0.47 filth],... 'ForegroundColor',col3,... 'BackgroundColor',col1,... lf{:},... 'Callback',@(ob,ev)update_filt(get(sib('dirs'),'Userdata')),... 'String',char(gencode_rvalue(vals{2}{ci}{1})),... 'UserData',uid); cfiltp = cfiltp+filth; end end end % Buttons uipanel pcnt = uipanel(fg,... 'units','normalized',... 'Position',pcntp,... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'BorderType','none',... 'Tag','pcnt'); % get cwidth for buttons tmp=uicontrol('style','text','string',repmat('X',[1,50]),bf{:},... 'units','normalized','visible','off'); fnp = get(tmp,'extent'); delete(tmp); cw = 3*fnp(3)/50; % Help uicontrol(pcnt,... 'Style','pushbutton',... 'units','normalized',... 'Position',[0.02 0 cw 1],... bf{:},... 'Callback',@heelp,... 'tag','?',... 'ForegroundColor',col3,... 'BackgroundColor',col1,... 'String','?',... 'ToolTipString','Show Help'); uicontrol(pcnt,... 'Style','pushbutton',... 'units','normalized',... 'Position',[0.03+cw 0 cw 1],... bf{:},... 'Callback',@editwin,... 'tag','Ed',... 'ForegroundColor',col3,... 'BackgroundColor',col1,... 'String','Ed',... 'ToolTipString','Edit Selected Files'); uicontrol(pcnt,... 'Style','pushbutton',... 'units','normalized',... 'Position',[0.04+2*cw 0 cw 1],... bf{:},... 'Callback',@select_rec,... 'tag','Rec',... 'ForegroundColor',col3,... 'BackgroundColor',col1,... 'String','Rec',... 'ToolTipString','Recursively Select Files with Current Filter'); % Done dne = uicontrol(pcnt,... 'Style','pushbutton',... 'units','normalized',... 'Position',[0.05+3*cw 0 0.44-3*cw 1],... bf{:},... 'Callback',@(h,e)delete(h),... 'tag','D',... 'ForegroundColor',col3,... 'BackgroundColor',col1,... 'String','Done',... 'Enable','off',... 'DeleteFcn',@null); % Filter label uicontrol(pcnt,... 'style','text',... 'units','normalized',... 'Position',[0.51 0 0.08 1],... 'HorizontalAlignment','left',... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'String','Filter'); % Filter Button uicontrol(pcnt,... 'Style','pushbutton',... 'units','normalized',... 'Position',[0.59 0 0.08 1],... bf{:},... 'ForegroundColor',col3,... 'BackgroundColor',col1,... 'Callback',@clearfilt,... 'String','Reset'); % Filter uicontrol(pcnt,... 'style','edit',... 'units','normalized',... 'Position',[0.68 0 0.3 1],... 'ForegroundColor',col3,... 'BackgroundColor',col1,... lf{:},... 'Callback',@(ob,ev)update(get(sib('dirs'),'Userdata')),... 'tag','regexp',... 'String',filt_or,... 'UserData',sfilt); % File/Dir uipanel pfd = uipanel(fg,... 'units','normalized',... 'Position',pfdp,... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'BorderType','none',... 'Tag','pfd'); % Directories db = uicontrol(pfd,... 'style','listbox',... 'units','normalized',... 'Position',[0.02 0 0.47 1],... lf{:},... 'Callback',@click_dir_box,... 'tag','dirs',... 'BackgroundColor',col1,... 'ForegroundColor',col3,... 'Max',1,... 'Min',0,... 'String','',... 'ToolTipString','Navigate Directories',... 'UserData',wd,... 'Value',1); % Files tmp = uicontrol(pfd,... 'style','listbox',... 'units','normalized',... 'Position',[0.51 0 0.47 1],... lf{:},... 'Callback',@click_file_box,... 'tag','files',... 'BackgroundColor',col1,... 'ForegroundColor',col3,... 'UserData',n,... 'Max',10240,... 'Min',0,... 'String','',... 'ToolTipString','Select Items',... 'Value',1); c0 = uicontextmenu('Parent',fg); set(tmp,'uicontextmenu',c0); uimenu('Label','Select All', 'Parent',c0,'Callback',@select_all); updatedir_fun = @(ob,ev)(update(char(cpath(subsref(get(ob,'String'),substruct('()',{get(ob,'Value')})))))); % Drives if ispc % get fh for lists tmp=uicontrol('style','text','string','X',lf{:},... 'units','normalized','visible','off'); fnp = get(tmp,'extent'); delete(tmp); fh = 2*fnp(4); % Heuristics: why do we need 2* sz = get(db,'Position'); sz(4) = sz(4)-fh-0.05; set(db,'Position',sz); uicontrol(pfd,... 'style','text',... 'units','normalized',... 'Position',[0.02 1-fh-0.01 0.10 fh],... 'HorizontalAlignment','left',... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'String','Drive'); uicontrol(pfd,... 'style','popupmenu',... 'units','normalized',... 'Position',[0.12 1-fh-0.01 0.37 fh],... lf{:},... 'Callback',updatedir_fun,... 'tag','drive',... 'BackgroundColor',col1,... 'ForegroundColor',col3,... 'String',listdrives(false),... 'Value',1); end % Directories uipanel pdir = uipanel(fg,... 'units','normalized',... 'Position',pdirp,... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'BorderType','none',... 'Tag','pdir'); [pd,vl] = prevdirs(wd); % Previous dirs uicontrol(pdir,... 'style','popupmenu',... 'units','normalized',... 'Position',[0.12 0.05 0.86 .95*1/3],... lf{:},... 'Callback',updatedir_fun,... 'tag','previous',... 'BackgroundColor',col1,... 'ForegroundColor',col3,... 'String',pd,... 'Value',vl); uicontrol(pdir,... 'style','text',... 'units','normalized',... 'Position',[0.02 0 0.10 .95*1/3],... 'HorizontalAlignment','left',... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'String','Prev'); % Parent dirs uicontrol(pdir,... 'style','popupmenu',... 'units','normalized',... 'Position',[0.12 1/3+.05 0.86 .95*1/3],... lf{:},... 'Callback',updatedir_fun,... 'tag','pardirs',... 'BackgroundColor',col1,... 'ForegroundColor',col3,... 'String',pardirs(wd)); uicontrol(pdir,... 'style','text',... 'units','normalized',... 'Position',[0.02 1/3 0.10 .95*1/3],... 'HorizontalAlignment','left',... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'String','Up'); % Directory uicontrol(pdir,... 'style','edit',... 'units','normalized',... 'Position',[0.12 2/3 0.86 .95*1/3],... lf{:},... 'Callback',@(ob,ev)(update(char(cpath({get(ob,'String')})))),... 'tag','edit',... 'BackgroundColor',col1,... 'ForegroundColor',col3,... 'String',''); uicontrol(pdir,... 'style','text',... 'units','normalized',... 'Position',[0.02 2/3 0.10 .95*1/3],... 'HorizontalAlignment','left',... lf{:},... 'BackgroundColor',get(fg,'Color'),... 'ForegroundColor',col3,... 'String','Dir'); resize_fun(fg); set(fg, 'ResizeFcn',@resize_fun); update(wd) checkdone('Initial selection.'); set(fg,'windowstyle', 'modal'); waitfor(dne); drawnow; if ishandle(sel) t = get(sel,'String'); if isempty(t) t = {''}; elseif any(strcmp({sfilt.tfilt.typ},'dir')) % canonicalise non-empty folder selection t = cpath(t, {pwd}); end ok = 1; end if ishandle(fg), delete(fg); end drawnow; return; %======================================================================= %======================================================================= function drivestr = listdrives(reread) persistent mydrivestr; if isempty(mydrivestr) || (nargin > 0 && reread) driveLett = strcat(cellstr(char(('C':'Z')')), ':'); dsel = cellfun(@(dl)exist([dl '\'],'dir'),driveLett)~=0; mydrivestr = driveLett(dsel); end drivestr = mydrivestr; %======================================================================= %======================================================================= function [d,mch] = prevdirs(d) persistent pd if ~iscell(pd), pd = {}; end if nargin == 0 d = pd; else if ~iscell(d) d = cellstr(d); end d = unique(d(:)); mch = cellfun(@(d1)find(strcmp(d1,pd)), d, 'UniformOutput',false); sel = cellfun(@isempty, mch); npd = numel(pd); pd = [pd(:);d(sel)]; mch = [mch{~sel} npd+(1:nnz(sel))]; d = pd; end return; %======================================================================= %======================================================================= function pd = pardirs(wd) pd1 = pathparts(cellstr(wd)); if ispc pd = cell(numel(pd1{1}),1); pd{end} = pd1{1}{1}; for k = 2:numel(pd1{1}) pd{end-k+1} = fullfile(pd1{1}{1:k}); end else pd = cell(numel(pd1{1})+1,1); pd{end} = filesep; for k = 1:numel(pd1{1}) pd{end-k} = fullfile(filesep,pd1{1}{1:k}); end end %======================================================================= %======================================================================= function click_dir_box(lb,varargin) c = get_current_char; if isempty(c) || isequal(c,char(13)) vl = get(lb,'Value'); str = get(lb,'String'); pd = get(sib('edit'),'String'); sel = str{vl}; if strcmp(sel,'..') % Parent directory [dr, odr] = fileparts(pd); elseif strcmp(sel,'.') % Current directory dr = pd; odr = ''; else dr = fullfile(pd,sel); odr = ''; end update(dr); if ~isempty(odr) % If moving up one level, try to set focus on previously visited % directory cdrs = get(lb, 'String'); dind = find(strcmp(odr, cdrs)); if ~isempty(dind) set(lb, 'Value',dind(1)); end end end return; %======================================================================= %======================================================================= function update(dr) lb = sib('dirs'); if nargin<1 dr = get(lb,'UserData'); end [f,d] = listfiles(dr,getfilt); if isempty(d) dr = get(lb,'UserData'); [f,d] = listfiles(dr,getfilt); else set(lb,'UserData',dr); end set(lb,'Value',1,'String',d); set(sib('files'),'Value',1,'String',f); set(sib('pardirs'),'String',pardirs(dr),'Value',1); [ls,mch] = prevdirs(dr); set(sib('previous'),'String',ls,'Value',mch); set(sib('edit'),'String',dr); if ispc && numel(dr)>1 && dr(2)==':' str = char(get(sib('drive'),'String')); mch = find(lower(str(:,1))==lower(dr(1))); if isempty(mch) str = listdrives(true); cstr = char(str); mch = find(lower(cstr(:,1))==lower(dr(1))); if ~isempty(mch) set(sib('drive'),'String',str, 'Value',mch); end else set(sib('drive'),'Value',mch); end end return; %======================================================================= %======================================================================= function update_filt(dr) uid = get(gcbo, 'Userdata'); filt = get(sib('regexp'), 'Userdata'); valstr = get(gcbo, 'String'); [val, sts] = cfg_eval_valedit(valstr); citem = subsref(filt.tfilt(uid.cf).prms, uid.id); if ~sts % Try with quotes/brackets if citem.strtype == 's' valstr1 = sprintf('''%s''', valstr); else valstr1 = sprintf('[%s]', valstr); end [val, sts] = cfg_eval_valedit(valstr1); end if sts citem.val{1} = val; filt.tfilt(uid.cf).prms = subsasgn(filt.tfilt(uid.cf).prms, uid.id, citem); set(sib('regexp'), 'Userdata',filt); end set(gcbo, 'String',char(gencode_rvalue(citem.val{1}))); update(dr); %======================================================================= %======================================================================= function checkdone(selmsg) nsel = numel(get(sib('selected'),'String')); fb = sib('files'); lim = get(fb,'Userdata'); if nsel == 1, s = ''; else, s = 's'; end if isequal(lim,[0 inf]) msg('Selected %d file%s. (%s)',nsel,s,selmsg); elseif lim(2) == inf msg('Selected %d/[%d-...] file%s. (%s)',nsel,lim(1),s,selmsg); else msg('Selected %d/[%d-%d] file%s. (%s)',nsel,lim,s,selmsg); end if nsel>=lim(1) && (~isfinite(lim(2)) || nsel<=lim(2)) set(sib('D'),'Enable','on'); else set(sib('D'),'Enable','off'); end %======================================================================= %======================================================================= function select_all(varargin) lb = sib('files'); set(lb,'Value',1:numel(get(lb,'String'))); drawnow; click_file_box(lb); return; %======================================================================= %======================================================================= function click_file_box(lb,varargin) c = get_current_char; if isempty(c) || isequal(c, char(13)) vlo = get(lb,'Value'); if isempty(vlo) msg('Nothing selected'); return; end str = get(lb,'String'); dr = get(sib('edit'), 'String'); nsel = select(cellfun(@(str1)fullfile(dr,str1),str(vlo),'UniformOutput',false),false); vrem = setdiff(1:numel(str), vlo(1:nsel)); set(lb,'Value',min([vlo(1),numel(str)-nsel]),'String',str(vrem)); end return; %======================================================================= %======================================================================= function nsel = select(new,repl) if repl already = {}; else already = get(sib('selected'),'String'); end fb = sib('files'); lim = get(fb,'Userdata'); nnew = numel(new); nalready = numel(already); nsel = min(lim(2)-nalready,nnew); set(sib('selected'),'String',[already(:);new(1:nsel)],'Value',[]); if nsel == 1, s = ''; else, s = 's'; end checkdone(sprintf('Added %d/%d file%s.',nsel,nnew,s)); return; %======================================================================= %======================================================================= function select_rec(varargin) start = get(sib('edit'),'String'); filt = get(sib('regexp'),'Userdata'); filt.filt = {get(sib('regexp'), 'String')}; ptr = get(gcbf,'Pointer'); try set(gcbf,'Pointer','watch'); sel = select_rec1(start,filt,true); select(sel,false); end set(gcbf,'Pointer',ptr); %======================================================================= %======================================================================= function [f,d]=select_rec1(cdir,filt,cdflag) [f,d] = listfiles(cdir,filt,cdflag); if isempty(f) f = {}; else f = strcat(cdir,filesep,f); end dsel = cellfun(@(d1)any(strcmp(d1,{'.','..'})),d); if any(~dsel) d = strcat(cdir,filesep,d(~dsel)); [f1,d1] = cellfun(@(d1)select_rec1(d1,filt,cdflag),d,'UniformOutput',false); f = [f(:);cat(1,f1{:})]; d = [d(:);cat(1,d1{:})]; else d = {}; end %======================================================================= %======================================================================= function unselect(lb,varargin) vl = get(lb,'Value'); if isempty(vl)||(numel(vl)==1 && vl==0), return; end str = get(lb,'String'); msk = true(numel(str),1); msk(vl) = false; str2 = str(msk); set(lb,'Value',min(vl(1),numel(str2)),'String',str2); if nnz(~msk) == 1, s = ''; else s = 's'; end checkdone(sprintf('Unselected %d file%s.',numel(vl),s)); return; %======================================================================= %======================================================================= function unselect_all(varargin) lb = sib('selected'); set(lb,'Value',[],'String',{},'ListBoxTop',1); checkdone('Unselected all files.'); return; %======================================================================= %======================================================================= function [f,d] = listfiles(dr,filt,cdflag) try ob = sib('msg'); domsg = ~isempty(ob); catch domsg = false; end if domsg omsg = msg('Listing directory...'); end if nargin<3, cdflag = true; end if nargin<2, filt = ''; end if nargin<1, dr = '.'; end de = dir(dr); if isempty(de) d = {'..'}; f = {}; if domsg msg(omsg); end return end d = sort({de([de.isdir]).name})'; d = d(~strcmp(d,'.')); dfilt = mk_filter('dir'); d = do_filter(d,dfilt.tfilt.regex); % add back '..' if ~any(strcmp('..',d)) d = [{'..'}; d(:)]; end dsel = strcmp({filt.tfilt.typ},'dir'); if any(dsel) fd = d(~strcmp('..',d)); if cdflag && ~any(strcmp('.',fd)) fd = [{'.'}; fd(:)]; end % reset regex filter filt.tfilt(dsel).regex = {'.*'}; else fd = {}; end if any(~dsel) ff = {de(~[de.isdir]).name}'; else ff = {}; end f = [sort(fd(:)); sort(ff(:))]; if ~isempty(f) if domsg msg('Filtering %d files...',numel(f)); end f = sort(do_filter(f,filt.filt)); if domsg msg('Listing %d files...',numel(f)); end f1 = cell(size(filt.tfilt)); i1 = cell(size(filt.tfilt)); for k = 1:numel(filt.tfilt) [f11,i11] = do_filter(f,filt.tfilt(k).regex); if isempty(f11)||isempty(filt.tfilt(k).fun) f1{k} = f11; i1{k} = i11; else [unused,prms] = harvest(filt.tfilt(k).prms, filt.tfilt(k).prms, false, false); [f1{k},i12] = filt.tfilt(k).fun('list',dr,f11,prms); i1{k} = i11(i12); end end % files might have been matched by multiple filters. Sort into roughly % alphabetical order before removing duplicates with 'stable' option. f = cat(1,f1{:}); o = cat(1,i1{:}); % [un,so] = sort(o); % f = unique(f(so), 'stable'); [un,fi,fj] = unique(f); ufi = unique(fi(fj)); [un,so] = sort(o(ufi)); f = f(ufi(so)); end d = unique(d(:)); if domsg msg(omsg); end return; %======================================================================= %======================================================================= function pp = pathparts(p) % parse paths in cellstr p % returns cell array of path component cellstr arrays % For PC (WIN) targets, both '\' and '/' are accepted as filesep, similar % to MATLAB fileparts if ispc fs = '\\/'; else fs = filesep; end pp = cellfun(@(p1)textscan(p1,'%s','delimiter',fs,'MultipleDelimsAsOne',1),p); if ispc for k = 1:numel(pp) if ~isempty(regexp(pp{k}{1}, '^[a-zA-Z]:$', 'once')) pp{k}{1} = strcat(pp{k}{1}, filesep); elseif ~isempty(regexp(p{k}, '^\\\\', 'once')) pp{k}{1} = strcat(filesep, filesep, pp{k}{1}); end end end %======================================================================= %======================================================================= function t = cpath(t,d) % canonicalise paths to full path names, removing xxx/./yyy and xxx/../yyy % constructs % t must be a cell array of (relative or absolute) paths, d must be a % single cell containing the base path of relative paths in t if ispc % valid absolute paths % Allow drive letter or UNC path mch = '^([a-zA-Z]:)|(\\\\[^\\]*)'; else mch = '^/'; end if (nargin<2)||isempty(d), d = {pwd}; end % Find partial paths, prepend them with d ppsel = cellfun(@isempty, regexp(t,mch,'once')); t(ppsel) = cellfun(@(t1)fullfile(d{1},t1),t(ppsel),'UniformOutput',false); % Break paths into cell lists of folder names pt = pathparts(t); % Remove single '.' folder names sd = cellfun(@(pt1)strcmp(pt1,'.'),pt,'UniformOutput',false); for cp = 1:numel(pt) pt{cp} = pt{cp}(~sd{cp}); end % Go up one level for '..' folders, don't remove drive letter/server name % from PC path if ispc ptstart = 2; else ptstart = 1; end for cp = 1:numel(pt) tmppt = {}; for cdir = ptstart:numel(pt{cp}) if strcmp(pt{cp}{cdir},'..') tmppt = tmppt(1:end-1); else tmppt{end+1} = pt{cp}{cdir}; end end if ispc pt{cp} = [pt{cp}(1) tmppt]; else pt{cp} = tmppt; end end % Assemble paths if ispc t = cellfun(@(pt1)fullfile(pt1{:}),pt,'UniformOutput',false); else t = cellfun(@(pt1)fullfile(filesep,pt1{:}),pt,'UniformOutput',false); end %======================================================================= %======================================================================= function clearfilt(varargin) set(sib('regexp'),'String','.*'); update; return; %======================================================================= %======================================================================= function re = getfilt % Get filter parameters from GUI widgets. ob = sib('regexp'); re = get(ob,'UserData'); re.filt = {get(ob,'String')}; return; %======================================================================= %======================================================================= function [f,ind] = do_filter_exp(f,filt) % Filter a cellstr list of filenames by typ/regexp and filter handlers. % FORMAT [f,ind] = do_filter_exp(f,filt) % f - cellstr list of filenames % filt - a filter struct as returned by mk_filter % ind - index into f_input such that f_output = f_input(ind) if (isempty(filt.filt) || any(strcmp(filt.filt,'.*'))) ind0 = (1:numel(f))'; else [f,ind0] = do_filter(f,filt.filt); end ind1 = cell(size(filt.tfilt)); for k = 1:numel(filt.tfilt) [f1, ind1{k}] = do_filter(f,filt.tfilt(k).regex); if ~(isempty(f1) || isempty(filt.tfilt(k).fun)) [unused,prms] = harvest(filt.tfilt(k).prms, filt.tfilt(k).prms, false, false); [unused,ind2] = filt.tfilt(k).fun('filter',f1,prms); ind1{k} = ind1{k}(ind2); end end sel = unique(cat(1,ind1{:})); ind = ind0(sel); f = f(sel); return %======================================================================= %======================================================================= function [f,ind] = do_filter(f,filt) % Filter a cellstr list of filenames with a regular expression filter. % FORMAT [f,ind] = do_filter(f,filt) % f - cellstr list of filenames % filt - cellstr list of filter expressions (these will be ORed together). % ind - index into f_input such that f_output = f_input(ind) filt_or = sprintf('(%s)|',filt{:}); t1 = regexp(f,filt_or(1:end-1)); if numel(f)==1 && ~iscell(t1), t1 = {t1}; end ind = find(~cellfun(@isempty,t1)); f = f(ind); return; %======================================================================= %======================================================================= function sfilt=mk_filter(typ,filt,varargin) % Create a filter structure for a specific typ and set of parameters. For % each typ, parameters should be a struct of parameter values with % fieldnames as defined by the tags of the registered cfg_entry items. if nargin<2, filt = '.*'; end if nargin<1, typ = 'any'; end sfilt.tfilt = reg_filter(typ); for k = 1:numel(sfilt.tfilt) if nargin >= k+2 sfilt.tfilt(k).prms = initialise(sfilt.tfilt(k).prms, varargin{k}, false); end end sfilt.filt = cellstr(filt); return %======================================================================= %======================================================================= function filt = reg_filter(typ,filt,cflag,fun,prms) % Store and retrieve filter definitions % FORMAT reg_filter(typ,filt,[fun[,prms]]) % typ - char identifying the filter % filt - filter expression (cellstr containing regular expressions). % This filter must cover both real filenames to be matched by % this type as well as expanded filenames as returned by the % associated handler function (if any). % cflag - case sensitivity - true for case sensitive matching, false % for case insensitive matching (default). % fun - optional function to expand typ specific file lists. Calling % syntax is % files = fun('list',dir,files,prms) % [files ind] = fun('filter',files,val1,prms) % dir - directory % files - cellstr of filenames (in 'list' mode: relative to % dir) % prms - optional parameters - a struct with fields with % tagnames according to registered prms cfg_entry % objects % ind - 'filter' mode: index vector into the output list of % files such that ind(k) = l if files_out{k} is % expanded from files_in{l}. % ind - 'list' mode: index vector into the input list of % files such that files_out = files_in(ind). % In 'list' mode, the handler may modify the list of % files (delete non-matching files, add indices to filenames % etc). In 'filter' mode, the handler may delete files from the % list, but must not modify filenames. % prms - optional cell array of cfg_entry items, describing parameter % inputs for handler function % FORMAT rfilt = reg_filter(typ) % typ - char or cellstr list of registered types % filt - list of matching filters. persistent lfilt; if isempty(lfilt) dtypes = {'any' ,'xml' ,'mat' ,'batch' ,'dir'}; dregex = {{'.*'} ,{'\.xml$'},{'\.mat$','\.txt$'},{'\.mat$','\.m$','\.xml$'},{'^[^.@]'}}; dcflag = false; dfun = {'' ,'' ,'' ,'' ,@local_isdir}; dprms = {cfg_branch}; lfilt = struct('typ',dtypes, 'regex',dregex, 'cflag',dcflag, 'fun',dfun, 'prms',dprms); end if nargin == 0 filt = lfilt; elseif nargin == 1 filt = struct('typ',{}, 'regex',{}, 'cflag',{}, 'fun',{}, 'prms',{}); ctyp = cellstr(typ); fsel = any(cell2mat(cellfun(@(t)strcmpi({lfilt.typ},t),ctyp,'UniformOutput',false)'),1); tsel = any(cell2mat(cellfun(@(t)strcmpi(ctyp,t),{lfilt.typ},'UniformOutput',false)'),1); if any(fsel) filt = lfilt(fsel); % sort filters into typ order [unused, st] = sort(ctyp(tsel)); [unused, sf] = sort({filt.typ}); filt = filt(st(sf)); end if any(~tsel) filt(end+1) = struct('typ','ad-hoc', 'regex',{ctyp(~tsel)}, 'cflag',true, 'fun','', 'prms',cfg_branch); end else nfilt = struct('typ',typ, 'regex',{cellstr(filt)}, 'cflag',false, 'fun','', 'prms',cfg_branch); if nargin > 2 nfilt.cflag = cflag; end if nargin > 3 nfilt.fun = fun; end if nargin > 4 if isa(prms, 'cfg_branch') nfilt.prms = prms; else nfilt.prms.val = prms; end end sel = strcmpi({lfilt.typ},typ); if any(sel) lfilt(sel) = nfilt; else lfilt = [lfilt nfilt]; end end return %======================================================================= %======================================================================= function editwin(varargin) [col1,col2,col3,lf,bf] = colours; fg = sib(mfilename); lb = sib('selected'); str = get(lb,'String'); ac = allchild(fg); acv = get(ac,'Visible'); h = uicontrol(fg,... 'Style','Edit',... 'units','normalized',... 'String',str,... lf{:},... 'Max',2,... 'Tag','EditWindow',... 'HorizontalAlignment','Left',... 'ForegroundColor',col3,... 'BackgroundColor',col1,... 'Position',[0.01 0.08 0.98 0.9],... 'Userdata',struct('ac',{ac},'acv',{acv})); ea = uicontrol(fg,... 'Style','pushbutton',... 'units','normalized',... 'Position',[.01 .01,.32,.07],... bf{:},... 'Callback',@editdone,... 'tag','EditWindowAccept',... 'ForegroundColor',col3,... 'BackgroundColor',col1,... 'String','Accept'); ee = uicontrol(fg,... 'Style','pushbutton',... 'units','normalized',... 'Position',[.34 .01,.32,.07],... bf{:},... 'Callback',@editeval,... 'tag','EditWindowEval',... 'ForegroundColor',col3,... 'BackgroundColor',col1,... 'String','Eval'); ec = uicontrol(fg,... 'Style','pushbutton',... 'units','normalized',... 'Position',[.67 .01,.32,.07],... bf{:},... 'Callback',@editclear,... 'tag','EditWindowCancel',... 'ForegroundColor',col3,... 'BackgroundColor',col1,... 'String','Cancel'); set(ac,'visible','off'); %======================================================================= %======================================================================= function editeval(varargin) ob = sib('EditWindow'); str = get(ob, 'String'); if isempty(str), return, end if ~isempty(str) [out, sts] = cfg_eval_valedit(char(str)); if sts && (iscellstr(out) || ischar(out)) set(ob, 'String', cellstr(out)); else fgc = get(ob, 'ForegroundColor'); set(ob, 'ForegroundColor', 'red'); pause(1); set(ob, 'ForegroundColor', fgc); end end %======================================================================= %======================================================================= function editdone(varargin) ob = sib('EditWindow'); str = cellstr(get(ob,'String')); if isempty(str) || isempty(str{1}) str = {}; else dstr = deblank(str); if ~isequal(str, dstr) c = questdlg(['Some of the filenames contain trailing blanks. This may ' ... 'be due to copy/paste of strings between MATLAB and the ' ... 'edit window. Do you want to remove any trailing blanks?'], ... 'Trailing Blanks in Filenames', ... 'Remove', 'Keep', 'Remove'); switch lower(c) case 'remove' str = dstr; end end filt = getfilt; filt.filt = {}; [p, n, e] = cellfun(@fileparts, str, 'uniformoutput',false); fstr = strcat(n, e); [fstr1, fsel] = do_filter_exp(fstr, filt); str = str(fsel); end select(str,true); editclear; %======================================================================= %======================================================================= function editclear(varargin) acs = get(sib('EditWindow'),'Userdata'); delete(findobj(sib(mfilename),'-regexp','Tag','^EditWindow.*')); set(acs.ac,{'Visible'},acs.acv); %======================================================================= %======================================================================= function heelp(varargin) [col1,col2,col3,lf,bf] = colours; fg = sib(mfilename); ac = allchild(fg); acv = get(ac,'Visible'); set(ac,'Visible','off'); t = uicontrol(fg,... 'style','listbox',... 'units','normalized',... 'Position',[0.01 0.1 0.98 0.9],... lf{:},... 'BackgroundColor',col2,... 'ForegroundColor',col3,... 'Max',0,... 'Min',0,... 'tag','HelpWin',... 'String',' ',... 'Userdata',struct('ac',{ac},'acv',{acv})); ea = uicontrol(fg,... 'Style','pushbutton',... 'units','normalized',... 'Position',[.01 .01,.99,.07],... bf{:},... 'Callback',@helpclear,... 'tag','HelpWinClear',... 'ForegroundColor',col3,... 'BackgroundColor',col1,... 'String','Close'); str = cfg_justify(t, {[... 'File Selection help. You can return to selecting files via the right mouse button (the "Done" option). '],... '',[... 'The panel at the bottom shows files that are already selected. ',... 'Clicking a selected file will un-select it. To un-select several, you can ',... 'drag the cursor over the files, and they will be gone on release. ',... 'You can use the right mouse button to un-select everything.'],... '',[... 'Directories are navigated by editing the name of the current directory (where it says "Dir"), ',... 'by going to one of the previously entered directories ("Prev"), ',... 'by going to one of the parent directories ("Up") or by navigating around ',... 'the parent or subdirectories listed in the left side panel.'],... '',[... 'Files matching the filter ("Filt") are shown in the panel on the right. ',... 'These can be selected by clicking or dragging. Use the right mouse button if ',... 'you would like to select all files. Note that when selected, the files disappear ',... 'from this panel. They can be made to reappear by re-specifying the directory ',... 'or the filter. '],... '',[... 'Both directory and file lists can also be browsed by typing the leading ',... 'character(s) of a directory or file name.'],... '',[... 'Note that the syntax of the filter differs from that used by most other file selectors. ',... 'The filter works using so called ''regular expressions''. Details can be found in the ',... 'MATLAB help text on ''regexp''. ',... 'The following is a list of symbols with special meaning for filtering the filenames:'],... ' ^ start of string',... ' $ end of string',... ' . any character',... ' \ quote next character',... ' * match zero or more',... ' + match one or more',... ' ? match zero or one, or match minimally',... ' {} match a range of occurrances',... ' [] set of characters',... ' [^] exclude a set of characters',... ' () group subexpression',... ' \w match word [a-z_A-Z0-9]',... ' \W not a word [^a-z_A-Z0-9]',... ' \d match digit [0-9]',... ' \D not a digit [^0-9]',... ' \s match white space [ \t\r\n\f]',... ' \S not a white space [^ \t\r\n\f]',... ' \ exact word match',... '',[... 'The recursive selection button (Rec) allows files matching the regular expression to ',... 'be recursively selected. If there are many directories to search, then this can take ',... 'a while to run.'],... '',[... 'There is also an edit button (Ed), which allows you to edit your selection of files. ',... 'When you are done, then use the menu-button of your mouse to either cancel or accept your changes.'],''}); set(t,'String',str); return; %======================================================================= %======================================================================= function helpclear(ob,varargin) acs = get(sib('HelpWin'),'Userdata'); delete(findobj(sib(mfilename),'-regexp','Tag','^HelpWin.*')); set(acs.ac,{'Visible'},acs.acv); %======================================================================= %======================================================================= function [c1,c2,c3,lf,bf] = colours c1 = [1 1 1]; c2 = [1 1 1]; c3 = [0 0 0]; lf = cfg_get_defaults('cfg_ui.lfont'); bf = cfg_get_defaults('cfg_ui.bfont'); if isempty(lf) lf = {'FontName',get(0,'FixedWidthFontName'), ... 'FontWeight','normal', ... 'FontAngle','normal', ... 'FontSize',14, ... 'FontUnits','points'}; end if isempty(bf) bf = {'FontName',get(0,'FixedWidthFontName'), ... 'FontWeight','normal', ... 'FontAngle','normal', ... 'FontSize',14, ... 'FontUnits','points'}; end %======================================================================= %======================================================================= function [pselp, pfiltp, pcntp, pfdp, pdirp] = panelpositions(fg, sellines, filtlines) if nargin == 1 na = numel(get(findobj(fg,'Tag','selected'),'String')); n = get(findobj(fg,'Tag','files'),'Userdata'); sellines = min([max([n(2) na]), 4]); sfilt = get(findobj(fg,'Tag','regexp'),'Userdata'); filtlines = sum(arrayfun(@(f)numel(f.prms.val), sfilt.tfilt)); end lf = cfg_get_defaults('cfg_ui.lfont'); bf = cfg_get_defaults('cfg_ui.bfont'); % Create dummy text to estimate character height t=uicontrol('style','text','string','Xg','units','normalized','visible','off',lf{:}); lfh = 1.05*get(t,'extent'); delete(t) t=uicontrol('style','text','string','Xg','units','normalized','visible','off',bf{:}); bfh = 1.05*get(t,'extent'); delete(t) % panel heights % 3 lines for directory, parent and prev directory list % variable height for dir/file navigation % 1 line for buttons and regexp filter % filtlines for extra filter input % sellines plus scrollbar for selected files % build up from bottom to top pselh = sellines*lfh(4) + 1.2*lfh(4); pselp = [0 0 1 pselh]; pfilth = filtlines*bfh(4); pfiltp = [0 pselh 1 pfilth]; pcnth = 1*bfh(4); pcntp = [0 pselh+pfilth 1 pcnth]; pdirh = 3*lfh(4); pdirp = [0 1-pdirh 1 pdirh]; pfdh = 1-(pselh+pfilth+pcnth+pdirh); pfdp = [0 pselh+pfilth+pcnth 1 pfdh]; %======================================================================= %======================================================================= function resize_fun(fg,varargin) [pselp, pfiltp, pcntp, pfdp, pdirp] = panelpositions(fg); if pfdp(4) <= 0 pfdp(4)=eps; end set(findobj(fg,'Tag','msg'), 'Position',pselp); if pfiltp(4) > 0 set(findobj(fg,'Tag','pfilt'), 'Position',pfiltp); end set(findobj(fg,'Tag','pcnt'), 'Position',pcntp); set(findobj(fg,'Tag','pfd'), 'Position',pfdp); set(findobj(fg,'Tag','pdir'), 'Position',pdirp); return; %======================================================================= %======================================================================= function null(varargin) %======================================================================= %======================================================================= function omsg = msg(varargin) ob = sib('msg'); omsg = get(ob,'Title'); set(ob,'Title',sprintf(varargin{:})); drawnow; return; %======================================================================= %======================================================================= function c = get_current_char fg = sib(mfilename); c = get(fg, 'CurrentCharacter'); if ~isempty(c) % reset CurrentCharacter set(fg, 'CurrentCharacter', char(13)); end %======================================================================= %======================================================================= function obj = sib(tag) persistent fg; if isempty(fg) || ~ishandle(fg) fg = findobj(0,'-depth',1,'Tag',mfilename); end obj = findobj(fg,'Tag',tag); return; %======================================================================= %======================================================================= function varargout = local_isdir(cmd,varargin) switch lower(cmd) case 'list' d = dir(varargin{1}); dn = {d([d.isdir]).name}'; [varargout{1:2}] = intersect(varargin{2}, dn); case 'filter' varargout{1} = varargin{1}; varargout{2} = 1:numel(varargout{1}); end %======================================================================= %=======================================================================