diff --git a/MainScripts/license_uipickfiles.txt b/MainScripts/license_uipickfiles.txt new file mode 100644 index 0000000000000000000000000000000000000000..99a4b934ca091116f4b61c130bd73558f91ee526 --- /dev/null +++ b/MainScripts/license_uipickfiles.txt @@ -0,0 +1,25 @@ +Copyright (c) 2007, Douglas M. Schwarz +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution +* Neither the name of nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MainScripts/uipickfiles.m b/MainScripts/uipickfiles.m new file mode 100644 index 0000000000000000000000000000000000000000..fd72808c08dc0192ca5949527bfa89390a2c9c15 --- /dev/null +++ b/MainScripts/uipickfiles.m @@ -0,0 +1,1982 @@ +function out = uipickfiles(varargin) +%uipickfiles: GUI program to select files and/or folders. +% +% Syntax: +% files = uipickfiles('PropertyName',PropertyValue,...) +% +% The current folder can be changed by operating in the file navigator: +% double-clicking on a folder in the list or pressing Enter to move further +% down the tree, using the popup menu, clicking the up arrow button or +% pressing Backspace to move up the tree, or typing a path in the box to +% move to any folder. Right-clicking on the path box (control-click on the +% Mac) will pop up a context menu listing previously-visited folders. These +% folders are listed in order of when they were last visited (most recent +% at the top) and the list is saved between calls to uipickfiles. The list +% can be cleared or its maximum length changed with the items at the bottom +% of the menu. Also included (and unclearable) are the user's home folder +% and one or more MATLAB startup folders. +% +% (Windows only: To go to a UNC-named resource you will have to type the +% UNC name in the path box, but all such visited resources will be +% remembered and listed along with the mapped drives.) The items in the +% file navigator can be sorted by name, modification date or size by +% clicking on the headers, though neither date nor size are displayed. All +% folders are considered to have zero size. +% +% Files can be added to the list by double-clicking or selecting files +% (non-contiguous selections are possible with the control key) and +% pressing the Add button. Control-F will select all the files listed in +% the navigator while Control-A will select everything (Command instead of +% Control on the Mac). Since double-clicking a folder will open it, +% folders can be added only by selecting them and pressing the Add button. +% Files/folders in the list can be removed or re-ordered. Recall button +% will insert into the Selected Files list whatever files were returned the +% last time uipickfiles was run. When finished, a press of the Done button +% will return the full paths to the selected items in a cell array, +% structure array or character array. If the Cancel button or the escape +% key is pressed then zero is returned. +% +% The figure can be moved and resized in the usual way and this position is +% saved and used for subsequent calls to uipickfiles. The default position +% can be restored by double-clicking in a vacant region of the figure. +% +% The following optional property/value pairs can be specified as arguments +% to control the indicated behavior: +% +% Property Value +% ---------- ---------------------------------------------------------- +% FilterSpec String to specify starting folder and/or file filter. +% Ex: 'C:\bin' will start up in that folder. '*.txt' +% will list only files ending in '.txt'. 'c:\bin\*.txt' will +% do both. Default is to start up in the current folder and +% list all files. Can be changed with the GUI. +% +% REFilter String containing a regular expression used to filter the +% file list. Ex: '\.m$|\.mat$' will list files ending in +% '.m' and '.mat'. Default is empty string. Can be used +% with FilterSpec and both filters are applied. Can be +% changed with the GUI. +% +% REDirs Logical flag indicating whether to apply the regular +% expression filter to folder names. Default is false which +% means that all folders are listed. Can be changed with the +% GUI. +% +% Type Two-column cell array where the first column contains file +% filters and the second column contains descriptions. If +% this property is specified an additional popup menu will +% appear below the File Filter and selecting an item will put +% that item into the File Filter. By default, the first item +% will be entered into the File Filter. For example, +% { '*.m', 'M-files' ; +% '*.mat', 'MAT-files' }. +% Can also be a cell vector of file filter strings in which +% case the descriptions will be the same as the file filters +% themselves. +% Must be a cell array even if there is only one entry. +% +% Prompt String containing a prompt appearing in the title bar of +% the figure. Default is 'Select files'. +% +% NumFiles Scalar or vector specifying number of files that must be +% selected. A scalar specifies an exact value; a two-element +% vector can be used to specify a range, [min max]. The +% function will not return unless the specified number of +% files have been chosen. Default is [] which accepts any +% number of files. +% +% Append Cell array of strings, structure array or char array +% containing a previously returned output from uipickfiles. +% Used to start up program with some entries in the Selected +% Files list. Any included files that no longer exist will +% not appear. Default is empty cell array, {}. +% +% Output String specifying the data type of the output: 'cell', +% 'struct' or 'char'. Specifying 'cell' produces a cell +% array of strings, the strings containing the full paths of +% the chosen files. 'Struct' returns a structure array like +% the result of the dir function except that the 'name' field +% contains a full path instead of just the file name. 'Char' +% returns a character array of the full paths. This is most +% useful when you have just one file and want it in a string +% instead of a cell array containing just one string. The +% default is 'cell'. +% +% All properties and values are case-insensitive and need only be +% unambiguous. For example, +% +% files = uipickfiles('num',1,'out','ch') +% +% is valid usage. + +% Version: 1.22, 15 June 2020 +% Author: Douglas M. Schwarz +% Email: dmschwarz=ieee*org, dmschwarz=urgrad*rochester*edu +% Real_email = regexprep(Email,{'=','*'},{'@','.'}) + + +% Define properties and set default values. +prop.filterspec = '*'; +prop.refilter = ''; +prop.redirs = false; +prop.type = {}; +prop.prompt = 'Select files'; +prop.numfiles = []; +prop.append = []; +prop.output = 'cell'; + +% Process inputs and set prop fields. +prop = parsepropval(prop,varargin{:}); + +% Validate FilterSpec property. +if isempty(prop.filterspec) + prop.filterspec = '*'; +end +if ~ischar(prop.filterspec) + error('FilterSpec property must contain a string.') +end + +% Validate REFilter property. +if ~ischar(prop.refilter) + error('REFilter property must contain a string.') +end + +% Validate REDirs property. +if ~isscalar(prop.redirs) + error('REDirs property must contain a scalar.') +end + +% Validate Type property. +if isempty(prop.type) +elseif iscellstr(prop.type) && isvector(prop.type) + prop.type = repmat(prop.type(:),1,2); +elseif iscellstr(prop.type) && size(prop.type,2) == 2 +else + error(['Type property must be empty or a cellstr vector or ',... + 'a 2-column cellstr matrix.']) +end + +% Validate Prompt property. +if ~ischar(prop.prompt) + error('Prompt property must contain a string.') +end + +% Validate NumFiles property. +if numel(prop.numfiles) > 2 || any(prop.numfiles < 0) + error('NumFiles must be empty, a scalar or two-element vector.') +end +prop.numfiles = unique(prop.numfiles); +if isequal(prop.numfiles,1) + numstr = 'Select exactly 1 item.'; +elseif length(prop.numfiles) == 1 + numstr = sprintf('Select exactly %d items.',prop.numfiles); +else + numstr = sprintf('Select %d to %d items.',prop.numfiles); +end + +% Validate Append property and initialize pick data. +if isstruct(prop.append) && isfield(prop.append,'name') + prop.append = {prop.append.name}; +elseif ischar(prop.append) + prop.append = cellstr(prop.append); +end +if isempty(prop.append) + file_picks = {}; + full_file_picks = {}; + dir_picks = repmat(dir(char(127)),0,1); % Create empty directory structure. +elseif iscellstr(prop.append) && isvector(prop.append) + num_items = length(prop.append); + file_picks = cell(1,num_items); + full_file_picks = cell(1,num_items); + dir_fn = fieldnames(repmat(dir(char(127)),0,1)); + dir_picks = repmat(cell2struct(cell(length(dir_fn),1),dir_fn(:)),... + num_items,1); + for item = 1:num_items + if fdexist(prop.append{item},'dir') && ... + ~any(strcmp(full_file_picks,prop.append{item})) + full_file_picks{item} = prop.append{item}; + [unused,fn,ext] = fileparts(prop.append{item}); + file_picks{item} = [fn,ext]; + path_c_tmp = path2cell(prop.append{item}); + temp = dir(cell2path(path_c_tmp(1:end-1))); + if ispc || ismac + thisdir = strcmpi({temp.name},[fn,ext]); + else + thisdir = strcmp({temp.name},[fn,ext]); + end + dir_picks(item) = temp(thisdir); + dir_picks(item).name = prop.append{item}; + elseif fdexist(prop.append{item},'file') && ... + ~any(strcmp(full_file_picks,prop.append{item})) + full_file_picks{item} = prop.append{item}; + [unused,fn,ext] = fileparts(prop.append{item}); + file_picks{item} = [fn,ext]; + dir_picks(item) = dir(prop.append{item}); + dir_picks(item).name = prop.append{item}; + else + continue + end + end + % Remove items which no longer exist. + missing = cellfun(@isempty,full_file_picks); + full_file_picks(missing) = []; + file_picks(missing) = []; + dir_picks(missing) = []; +else + error('Append must be a cell, struct or char array.') +end + +% Validate Output property. +legal_outputs = {'cell','struct','char'}; +out_idx = find(strncmpi(prop.output,legal_outputs,length(prop.output))); +if length(out_idx) == 1 + prop.output = legal_outputs{out_idx}; +else + error(['Value of ''Output'' property, ''%s'', is illegal or '... + 'ambiguous.'],prop.output) +end + + +% Set style preference for display of folders. +% 1 => folder icon before and filesep after +% 2 => bullet before and filesep after +% 3 => filesep after only +folder_style_pref = 1; +fsdata = set_folder_style(folder_style_pref); + +% Set style preference for context menu. +% 1 => home and logo icons before home and MATLAB folders +% 2 => no icons +cmenu_style_pref = 1; +csdata = set_cmenu_style(cmenu_style_pref); + +% Get canonical names of current directory and filter. +jUserDir = java.lang.System.setProperty('user.dir',pwd); +if any(prop.filterspec == '*') + jFile = java.io.File(prop.filterspec).getAbsoluteFile; + current_dir = char(jFile.getParentFile().getCanonicalPath()); + filter = char(jFile.getName()); +else + jFile = java.io.File(prop.filterspec).getCanonicalFile(); + if jFile.isDirectory() + current_dir = char(jFile); + filter = '*'; + else + jDir = java.io.File(jFile.getParent()); + if jDir.isDirectory() + current_dir = char(jDir); + filter = char(jFile.getName()); + else + java.lang.System.setProperty('user.dir',jUserDir); + error('Path does not exist.') + end + end +end +java.lang.System.setProperty('user.dir',jUserDir); + +% Initialize file lists. +re_filter = prop.refilter; +full_filter = fullfile(current_dir,filter); +network_volumes = {}; +[path_cell,new_network_vol] = path2cell(current_dir); +if fdexist(new_network_vol,'dir') + network_volumes = unique([network_volumes,{new_network_vol}]); +end +fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,[1 0 0],c)); +filenames = {fdir.name}'; +[filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + +% Initialize some data. +show_full_path = false; +nodupes = true; + +% Get history preferences and set history. +history = getpref('uipickfiles','history',struct('name',{},'time',{})); +default_history_size = 15; +history_size = getpref('uipickfiles','history_size',default_history_size); + +% Get favorites. +favorites = getpref('uipickfiles','favorites',{}); + +% Set history. +history = update_history(history,current_dir,now,history_size); + +% Get figure position preference and create figure. +gray = [220 220 220]/255; +if ispref('uipickfiles','figure_position') + fig_pos = getpref('uipickfiles','figure_position'); + create_fcn = ''; +else + fig_pos = [0 0 740 494]; + create_fcn = {@movegui,'center'}; +end +fig = figure('Position',fig_pos,... + 'MenuBar','none',... + 'WindowStyle','modal',... + 'Color',gray,... + 'Resize','on',... + 'NumberTitle','off',... + 'Name',prop.prompt,... + 'IntegerHandle','off',... + 'CloseRequestFcn',@cancel,... + 'CreateFcn',create_fcn,... + 'ButtonDownFcn',@reset_figure_size,... + 'KeyPressFcn',@keypressmisc,... + 'Visible','off'); +fig_color = fig.Color; + +% Set system-dependent items. +if ismac +% ver = char(java.lang.System.getProperty('os.version')); + set(fig,'DefaultUIControlFontName','Lucida Grande') + set(fig,'DefaultUIControlFontSize',13) + sort_ctrl_size = 12; + mod_key = 'command'; + action = 'Control-click'; +elseif ispc +% ver = str2double(java.lang.System.getProperty('os.version')); + set(fig,'DefaultUIControlFontName','Segoe UI') + set(fig,'DefaultUIControlFontSize',9) + sort_ctrl_size = 7; + mod_key = 'control'; + action = 'Right-click'; +else + set(fig,'DefaultUIControlFontName','Dialog') + sort_ctrl_size = get(fig,'DefaultUIControlFontSize') - 1; + mod_key = 'control'; + action = 'Right-click'; +end + +% Create uicontrols. +frame1 = uicontrol('Style','frame',... + 'BackgroundColor',fig_color,... + 'Position',[255 260 110 70]); +frame2 = uicontrol('Style','frame',... + 'BackgroundColor',fig_color,... + 'Position',[275 135 110 100]); + +navlist = uicontrol('Style','listbox',... + 'Position',[10 10 250 320],... + 'String',filenames,... + 'Value',[],... + 'BackgroundColor','w',... + 'Callback',@clicknav,... + 'KeyPressFcn',@keypressnav,... + 'Max',2); + +tri_up = repmat([1 1 1 1 0 1 1 1 1;1 1 1 0 0 0 1 1 1;1 1 0 0 0 0 0 1 1;... + 1 0 0 0 0 0 0 0 1],[1 1 3]); +tri_up(tri_up == 1) = NaN; +tri_down = tri_up(end:-1:1,:,:); +tri_null = NaN(4,9,3); +tri_icon = {tri_down,tri_null,tri_up}; +sort_state = [1 0 0]; +last_sort_state = [1 1 1]; +sort_cb = zeros(1,3); +sort_cb(1) = uicontrol('Style','checkbox',... + 'Position',[15 331 70 15],... + 'String','Name',... + 'FontSize',sort_ctrl_size,... + 'BackgroundColor',fig_color,... + 'Value',sort_state(1),... + 'CData',tri_icon{sort_state(1)+2},... + 'KeyPressFcn',@keypressmisc,... + 'Callback',{@sort_type,1}); +sort_cb(2) = uicontrol('Style','checkbox',... + 'Position',[85 331 70 15],... + 'String','Date',... + 'FontSize',sort_ctrl_size,... + 'BackgroundColor',fig_color,... + 'Value',sort_state(2),... + 'CData',tri_icon{sort_state(2)+2},... + 'KeyPressFcn',@keypressmisc,... + 'Callback',{@sort_type,2}); +sort_cb(3) = uicontrol('Style','checkbox',... + 'Position',[155 331 70 15],... + 'String','Size',... + 'FontSize',sort_ctrl_size,... + 'BackgroundColor',fig_color,... + 'Value',sort_state(3),... + 'CData',tri_icon{sort_state(3)+2},... + 'KeyPressFcn',@keypressmisc,... + 'Callback',{@sort_type,3}); + +pickslist = uicontrol('Style','listbox',... + 'Position',[380 10 350 320],... + 'String',file_picks,... + 'BackgroundColor','w',... + 'Callback',@clickpicks,... + 'KeyPressFcn',@keypresslist,... + 'Max',2,... + 'Value',[]); + +openbut = uicontrol('Style','pushbutton',... + 'Position',[265 295 90 30],... + 'String','Open',... + 'Enable','off',... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@open); + +arrow = [ ... + ' 1 '; + ' 10 '; + ' 10 '; + '000000000000'; + ' 10 '; + ' 10 '; + ' 1 ']; +cmap = NaN(128,3); +cmap(double('10'),:) = [0.5 0.5 0.5;0 0 0]; +arrow_im = NaN(7,76,3); +arrow_im(:,45:56,:) = ind2rgb(double(arrow),cmap); +add_cm = uicontextmenu; +add_cm_items(1) = uimenu(add_cm,... + 'Label','Add with path subfolders',... + 'Callback',{@add_with_subfolders,'pathsub'},... + 'Visible','off'); +add_cm_items(2) = uimenu(add_cm,... + 'Label','Add with subfolders',... + 'Callback',{@add_with_subfolders,'sub'},... + 'Visible','off'); +add_cm_items(3) = uimenu(add_cm,... + 'Label','<html>Add with <i>all</i> subfolders</html>',... + 'Separator','on',... + 'Callback',{@add_with_subfolders,'allsub'},... + 'Visible','off'); +addbut = uicontrol('Style','pushbutton',... + 'Position',[265 265 90 30],... + 'String','Add ',... + 'Enable','off',... + 'CData',arrow_im,... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@add,... + 'UIContextMenu',add_cm); + +removebut = uicontrol('Style','pushbutton',... + 'Position',[285 200 90 30],... + 'String','Remove',... + 'Enable','off',... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@remove); +moveupbut = uicontrol('Style','pushbutton',... + 'Position',[285 170 90 30],... + 'String','Move Up',... + 'Enable','off',... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@moveup); +movedownbut = uicontrol('Style','pushbutton',... + 'Position',[285 140 90 30],... + 'String','Move Down',... + 'Enable','off',... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@movedown); + +dir_popup = uicontrol('Style','popupmenu',... + 'Position',[10 350 225 20],... + 'BackgroundColor','w',... + 'String',path_cell,... + 'Value',length(path_cell),... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@dirpopup); + +uparrow = [ ... + ' 0 '; + ' 000 '; + '00000 '; + ' 0 '; + ' 0 '; + ' 0 '; + ' 000000']; +cmap = NaN(128,3); +cmap(double('0'),:) = [0 0 0]; +uparrow_im = ind2rgb(double(uparrow),cmap); +up_dir_but = uicontrol('Style','pushbutton',... + 'Position',[240 350 20 20],... + 'CData',uparrow_im,... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@dir_up_one,... + 'ToolTip','Go to parent folder'); +if length(path_cell) > 1 + up_dir_but.Enable = 'on'; +else + up_dir_but.Enable = 'off'; +end + +hist_cm = uicontextmenu; +pathbox = uicontrol('Style','edit',... + 'Position',[10 375 250 26],... + 'BackgroundColor','w',... + 'String',current_dir,... + 'HorizontalAlignment','left',... + 'TooltipString',[action,' to display folder history'],... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@change_path,... + 'UIContextMenu',hist_cm); +label1 = uicontrol('Style','text',... + 'Position',[10 401 250 16],... + 'String','Current Folder',... + 'HorizontalAlignment','center',... + 'BackgroundColor',fig_color,... + 'TooltipString',[action,' to display folder history'],... + 'UIContextMenu',hist_cm); +hist_menus = []; +make_history_cm(csdata) + +label2 = uicontrol('Style','text',... + 'Position',[10 440+36 80 17],... + 'String','File Filter',... + 'HorizontalAlignment','left',... + 'BackgroundColor',fig_color); +label3 = uicontrol('Style','text',... + 'Position',[100 440+36 160 17],... + 'String','Regular Expression Filter',... + 'HorizontalAlignment','left',... + 'BackgroundColor',fig_color); +label3.Position(3) = label3.Extent(3); +% 'String','Reg. Exp. Filter',... +showallfiles = uicontrol('Style','checkbox',... + 'Position',[270 420+32 110 20],... + 'String','Show All Files',... + 'Value',0,... + 'HorizontalAlignment','left',... + 'BackgroundColor',fig_color,... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@togglefilter); +showallfiles.Position(3) = showallfiles.Extent(3) + 20; +refilterdirs = uicontrol('Style','checkbox',... + 'Position',[270 420+10 100 20],... + 'String','RE Filter Dirs',... + 'Value',prop.redirs,... + 'HorizontalAlignment','left',... + 'BackgroundColor',fig_color,... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@toggle_refiltdirs); +refilterdirs.Position(3) = refilterdirs.Extent(3) + 20; +filter_ed = uicontrol('Style','edit',... + 'Position',[10 420+30 80 26],... + 'BackgroundColor','w',... + 'String',filter,... + 'HorizontalAlignment','left',... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@setfilspec); +refilter_ed = uicontrol('Style','edit',... + 'Position',[100 420+30 160 26],... + 'BackgroundColor','w',... + 'String',re_filter,... + 'HorizontalAlignment','left',... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@setrefilter); + +type_value = 1; +type_popup = uicontrol('Style','popupmenu',... + 'Position',[10 422 250 20],... + 'String','',... + 'BackgroundColor','w',... + 'Value',type_value,... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@filter_type_callback,... + 'Visible','off'); +if ~isempty(prop.type) + filter_ed.String = prop.type{type_value,1}; + setfilspec() + type_popup.String = prop.type(:,2); + type_popup.Visible = 'on'; +end + +viewfullpath = uicontrol('Style','checkbox',... + 'Position',[380 335 230 20],... + 'String','Show full paths',... + 'Value',show_full_path,... + 'HorizontalAlignment','left',... + 'BackgroundColor',fig_color,... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@showfullpath); +remove_dupes = uicontrol('Style','checkbox',... + 'Position',[380 360 280 20],... + 'String','Remove duplicates (as per full path)',... + 'Value',nodupes,... + 'HorizontalAlignment','left',... + 'BackgroundColor',fig_color,... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@removedupes); +recall_button = uicontrol('Style','pushbutton',... + 'Position',[665 335 65 30],... + 'String','Recall',... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@recall,... + 'ToolTip','Add previously selected items'); +label4 = uicontrol('Style','text',... + 'Position',[380 405 350 20],... + 'String','Selected Items',... + 'HorizontalAlignment','center',... + 'BackgroundColor',fig_color); +done_button = uicontrol('Style','pushbutton',... + 'Position',[280 80 80 30],... + 'String','Done',... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@done); +cancel_button = uicontrol('Style','pushbutton',... + 'Position',[280 30 80 30],... + 'String','Cancel',... + 'KeyPressFcn',@keypressmisc,... + 'Callback',@cancel); + +% If necessary, add warning about number of items to be selected. +num_files_warn = uicontrol('Style','text',... + 'Position',[380 385 350 16],... + 'String',numstr,... + 'ForegroundColor',[0.8 0 0],... + 'BackgroundColor',fig_color,... + 'HorizontalAlignment','center',... + 'Visible','off'); +if ~isempty(prop.numfiles) + num_files_warn.Visible = 'on'; +end + +resize() +% Make figure visible and hide handle. +set(fig,'HandleVisibility','off',... + 'Visible','on',... + 'ResizeFcn',@resize) + +% Wait until figure is closed. +uiwait(fig) + +% Compute desired output. +switch prop.output + case 'cell' + out = full_file_picks; + case 'struct' + out = dir_picks(:); + case 'char' + out = char(full_file_picks); + case 'cancel' + out = 0; +end + +% Update history preference. +setpref('uipickfiles','history',history) +if ~isempty(full_file_picks) && ~strcmp(prop.output,'cancel') + setpref('uipickfiles','full_file_picks',full_file_picks) +end + +% Update favorites preference. +setpref('uipickfiles','favorites',favorites) + +% Update figure position preference. +setpref('uipickfiles','figure_position',fig_pos) + + +% ----------------- Callback nested functions ---------------- + + function add(varargin) + values = navlist.Value; + for i = 1:length(values) + dir_pick = fdir(values(i)); + pick = dir_pick.name; + pick_full = fullfile(current_dir,pick); + dir_pick.name = pick_full; + if ~nodupes || ~any(strcmp(full_file_picks,pick_full)) + file_picks{end + 1} = pick; %#ok<AGROW> + full_file_picks{end + 1} = pick_full; %#ok<AGROW> + dir_picks(end + 1) = dir_pick; %#ok<AGROW> + end + end + % Added 3/2/14 - 2 lines + history = update_history(history,current_dir,now,history_size); + make_history_cm(csdata) + if show_full_path + set(pickslist,'String',full_file_picks,'Value',[]); + else + set(pickslist,'String',file_picks,'Value',[]); + end + set([removebut,moveupbut,movedownbut],'Enable','off'); + end + + function add_with_subfolders(varargin) + % Use final argument to determine exclusions for subdirs. + switch varargin{end} + case 'pathsub' + exclusions = '.@+p'; + case 'sub' + exclusions = '.'; + case 'allsub' + exclusions = ''; + end + values = navlist.Value; + for i = 1:length(values) + dir_pick = fdir(values(i)); + pick = dir_pick.name; + pick_full = fullfile(current_dir,pick); + subd = subdirs(pick_full,[],exclusions); + for j = 1:length(subd) + pick_full = subd{j}; + if ~nodupes || ~any(strcmp(full_file_picks,pick_full)) + [~,pick] = fileparts(pick_full); + dir_temp = dir(pick_full); + dir_pick = dir_temp(strcmp({dir_temp.name},'.')); + dir_pick.name = pick_full; + file_picks{end + 1} = pick; %#ok<AGROW> + full_file_picks{end + 1} = pick_full; %#ok<AGROW> + dir_picks(end + 1) = dir_pick; %#ok<AGROW> + end + end + end + % Added 3/2/14 - 2 lines + history = update_history(history,current_dir,now,history_size); + make_history_cm(csdata) + if show_full_path + set(pickslist,'String',full_file_picks,'Value',[]); + else + set(pickslist,'String',file_picks,'Value',[]); + end + set([removebut,moveupbut,movedownbut],'Enable','off'); + end + + function remove(varargin) + values = pickslist.Value; + file_picks(values) = []; + full_file_picks(values) = []; + dir_picks(values) = []; + top = pickslist.ListboxTop; + num_above_top = sum(values < top); + top = top - num_above_top; + num_picks = length(file_picks); + new_value = min(min(values) - num_above_top,num_picks); + if num_picks == 0 + new_value = []; + set([removebut,moveupbut,movedownbut],'Enable','off') + end + if show_full_path + set(pickslist,'String',full_file_picks,'Value',new_value,... + 'ListboxTop',top) + else + set(pickslist,'String',file_picks,'Value',new_value,... + 'ListboxTop',top) + end + end + + function open(varargin) + values = navlist.Value; + if fdir(values).isdir + fig.Pointer = 'watch'; + drawnow + % Convert 'My Documents' to 'Documents' when necessary. + if ispc && strcmp(fdir(values).name,'My Documents') + if isempty(dir(fullfile(current_dir,fdir(values).name))) + values = find(strcmp({fdir.name},'Documents')); + end + end + current_dir = fullfile(current_dir,fdir(values).name); + if ispc && ~isempty(regexpi(current_dir,'\.lnk')) && ... + is_shortcut_to_dir(current_dir) + JFile = java.io.File(current_dir); + sf = sun.awt.shell.ShellFolder.getShellFolder(JFile); + current_dir = char(sf.getLinkLocation()); + end +% history = update_history(history,current_dir,now,history_size); +% make_history_cm(csdata) + full_filter = fullfile(current_dir,filter); + path_cell = path2cell(current_dir); + fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,sort_state,c)); + filenames = {fdir.name}'; + [filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + set(dir_popup,'String',path_cell,'Value',length(path_cell)) + if length(path_cell) > 1 + up_dir_but.Enable = 'on'; + else + up_dir_but.Enable = 'off'; + end + pathbox.String = current_dir; + set(navlist,'ListboxTop',1,'Value',[],'String',filenames) + addbut.Enable = 'off'; + openbut.Enable = 'off'; + fig.Pointer = 'arrow'; + end + end + + function clicknav(varargin) + value = navlist.Value; + nval = length(value); + dbl_click_fcn = @add; + switch nval + case 0 + set([addbut,openbut],'Enable','off') + case 1 + addbut.Enable = 'on'; + if fdir(value).isdir + openbut.Enable = 'on'; + dbl_click_fcn = @open; + else + openbut.Enable = 'off'; + end + otherwise + addbut.Enable = 'on'; + openbut.Enable = 'off'; + end + if any([fdir(value).isdir]) + set(add_cm_items,'Visible','on') + else + set(add_cm_items,'Visible','off') + end + if strcmp(fig.SelectionType,'open') + dbl_click_fcn(); + end + end + + function keypressmisc(h,evt) %#ok<INUSL> + if strcmp(evt.Key,'escape') && isequal(evt.Modifier,cell(1,0)) + % Escape key means Cancel. + cancel() + end + end + + function keypressnav(h,evt) %#ok<INUSL> + if length(path_cell) > 1 && strcmp(evt.Key,'backspace') && ... + isequal(evt.Modifier,cell(1,0)) + % Backspace means go to parent folder. + dir_up_one() + elseif strcmp(evt.Key,'f') && isequal(evt.Modifier,{mod_key}) + % Control-F (Command-F on Mac) means select all files. + value = find(~[fdir.isdir]); + navlist.Value = value; + elseif strcmp(evt.Key,'rightarrow') && ... + isequal(evt.Modifier,cell(1,0)) + % Right arrow key means select the file. + add() + elseif strcmp(evt.Key,'escape') && isequal(evt.Modifier,cell(1,0)) + % Escape key means Cancel. + cancel() + end + end + + function keypresslist(h,evt) %#ok<INUSL> + if strcmp(evt.Key,'backspace') && isequal(evt.Modifier,cell(1,0)) + % Backspace means remove item from list. + remove() + elseif strcmp(evt.Key,'escape') && isequal(evt.Modifier,cell(1,0)) + % Escape key means Cancel. + cancel() + end + end + + function clickpicks(varargin) + value = pickslist.Value; + if isempty(value) + set([removebut,moveupbut,movedownbut],'Enable','off') + else + removebut.Enable = 'on'; + if min(value) == 1 + moveupbut.Enable = 'off'; + else + moveupbut.Enable = 'on'; + end + if max(value) == length(file_picks) + movedownbut.Enable = 'off'; + else + movedownbut.Enable = 'on'; + end + end + if strcmp(fig.SelectionType,'open') + remove(); + end + end + + function recall(varargin) + if ispref('uipickfiles','full_file_picks') + ffp = getpref('uipickfiles','full_file_picks'); + else + ffp = {}; + end + for i = 1:length(ffp) + if fdexist(ffp{i},'dir') && ... + (~nodupes || ~any(strcmp(full_file_picks,ffp{i}))) + full_file_picks{end + 1} = ffp{i}; %#ok<AGROW> + [unused,fn,ext] = fileparts(ffp{i}); + file_picks{end + 1} = [fn,ext]; %#ok<AGROW> + path_c_temp = path2cell(ffp{i}); + temp = dir(cell2path(path_c_temp(1:end-1))); + if ispc || ismac + thisdir = strcmpi({temp.name},[fn,ext]); + else + thisdir = strcmp({temp.name},[fn,ext]); + end + dir_picks(end + 1) = temp(thisdir); %#ok<AGROW> + dir_picks(end).name = ffp{i}; + elseif fdexist(ffp{i},'file') && ... + (~nodupes || ~any(strcmp(full_file_picks,ffp{i}))) + full_file_picks{end + 1} = ffp{i}; %#ok<AGROW> + [unused,fn,ext] = fileparts(ffp{i}); + file_picks{end + 1} = [fn,ext]; %#ok<AGROW> + dir_picks(end + 1) = dir(ffp{i}); %#ok<AGROW> + dir_picks(end).name = ffp{i}; + end + end + if show_full_path + set(pickslist,'String',full_file_picks,'Value',[]); + else + set(pickslist,'String',file_picks,'Value',[]); + end + set([removebut,moveupbut,movedownbut],'Enable','off'); + end + + function sort_type(h,evt,cb) %#ok<INUSL> + if sort_state(cb) + sort_state(cb) = -sort_state(cb); + last_sort_state(cb) = sort_state(cb); + else + sort_state = zeros(1,3); + sort_state(cb) = last_sort_state(cb); + end + set(sort_cb,{'CData'},tri_icon(sort_state + 2)') + + fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,sort_state,c)); + filenames = {fdir.name}'; + [filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + set(dir_popup,'String',path_cell,'Value',length(path_cell)) + if length(path_cell) > 1 + up_dir_but.Enable = 'on'; + else + up_dir_but.Enable = 'off'; + end + pathbox.String = current_dir; + set(navlist,'String',filenames,'Value',[]) + addbut.Enable = 'off'; + openbut.Enable = 'off'; + fig.Pointer = 'arrow'; + end + + function dirpopup(varargin) + value = dir_popup.Value; + container = path_cell{min(value + 1,length(path_cell))}; + path_cell = path_cell(1:value); + fig.Pointer = 'watch'; + drawnow + if ispc && value == 1 + current_dir = ''; + full_filter = filter; + drives = getdrives(network_volumes); + num_drives = length(drives); + temp = tempname; + mkdir(temp) + dir_temp = dir(temp); + rmdir(temp) + fdir = repmat(dir_temp(1),num_drives,1); + [fdir.name] = deal(drives{:}); + else + current_dir = cell2path(path_cell); +% history = update_history(history,current_dir,now,history_size); +% make_history_cm(csdata) + full_filter = fullfile(current_dir,filter); + fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,sort_state,c)); + end + filenames = {fdir.name}'; + selected = find(strcmp(filenames,container)); + [filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + set(dir_popup,'String',path_cell,'Value',length(path_cell)) + if length(path_cell) > 1 + up_dir_but.Enable = 'on'; + else + up_dir_but.Enable = 'off'; + end + pathbox.String = current_dir; + set(navlist,'String',filenames,'Value',selected) + addbut.Enable = 'off'; + fig.Pointer = 'arrow'; + end + + function dir_up_one(varargin) + value = length(path_cell) - 1; + container = path_cell{value + 1}; + path_cell = path_cell(1:value); + fig.Pointer = 'watch'; + drawnow + if ispc && value == 1 + current_dir = ''; + full_filter = filter; + drives = getdrives(network_volumes); + num_drives = length(drives); + temp = tempname; + mkdir(temp) + dir_temp = dir(temp); + rmdir(temp) + fdir = repmat(dir_temp(1),num_drives,1); + [fdir.name] = deal(drives{:}); + else + current_dir = cell2path(path_cell); +% history = update_history(history,current_dir,now,history_size); +% make_history_cm(csdata) + full_filter = fullfile(current_dir,filter); + fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,sort_state,c)); + end + filenames = {fdir.name}'; + selected = find(strcmp(filenames,container)); + [filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + set(dir_popup,'String',path_cell,'Value',length(path_cell)) + if length(path_cell) > 1 + up_dir_but.Enable = 'on'; + else + up_dir_but.Enable = 'off'; + end + pathbox.String = current_dir; + set(navlist,'String',filenames,'Value',selected) + addbut.Enable = 'off'; + fig.Pointer = 'arrow'; + end + + function change_path(varargin) + fig.Pointer = 'watch'; + drawnow + proposed_path = pathbox.String; + % Process any folders named '..'. + proposed_path_cell = path2cell(proposed_path); + ddots = strcmp(proposed_path_cell,'..'); + ddots(find(ddots) - 1) = true; + proposed_path_cell(ddots) = []; + proposed_path = cell2path(proposed_path_cell); + % Check for existance of folder. + if ~fdexist(proposed_path,'dir') + fig.Pointer = 'arrow'; + uiwait(errordlg(['Folder "',proposed_path,... + '" does not exist.'],'','modal')) + return + end + current_dir = proposed_path; +% history = update_history(history,current_dir,now,history_size); +% make_history_cm(csdata) + full_filter = fullfile(current_dir,filter); + [path_cell,new_network_vol] = path2cell(current_dir); + if fdexist(new_network_vol,'dir') + network_volumes = unique([network_volumes,{new_network_vol}]); + end + fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,sort_state,c)); + filenames = {fdir.name}'; + [filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + set(dir_popup,'String',path_cell,'Value',length(path_cell)) + if length(path_cell) > 1 + up_dir_but.Enable = 'on'; + else + up_dir_but.Enable = 'off'; + end + pathbox.String = current_dir; + set(navlist,'String',filenames,'Value',[]) + addbut.Enable = 'off'; + openbut.Enable = 'off'; + fig.Pointer = 'arrow'; + end + + function showfullpath(varargin) + show_full_path = viewfullpath.Value; + if show_full_path + pickslist.String = full_file_picks; + else + pickslist.String = file_picks; + end + end + + function removedupes(varargin) + nodupes = remove_dupes.Value; + if nodupes + num_picks = length(full_file_picks); + [unused,rev_order] = unique(full_file_picks(end:-1:1)); %#ok<SETNU> + order = sort(num_picks + 1 - rev_order); + full_file_picks = full_file_picks(order); + file_picks = file_picks(order); + dir_picks = dir_picks(order); + if show_full_path + set(pickslist,'String',full_file_picks,'Value',[]) + else + set(pickslist,'String',file_picks,'Value',[]) + end + set([removebut,moveupbut,movedownbut],'Enable','off') + end + end + + function moveup(varargin) + value = pickslist.Value; + removebut.Enable = 'on'; + n = length(file_picks); + omega = 1:n; + index = zeros(1,n); + index(value - 1) = omega(value); + index(setdiff(omega,value - 1)) = omega(setdiff(omega,value)); + file_picks = file_picks(index); + full_file_picks = full_file_picks(index); + dir_picks = dir_picks(index); + value = value - 1; + if show_full_path + set(pickslist,'String',full_file_picks,'Value',value) + else + set(pickslist,'String',file_picks,'Value',value) + end + if min(value) == 1 + moveupbut.Enable = 'off'; + end + movedownbut.Enable = 'on'; + end + + function movedown(varargin) + value = pickslist.Value; + removebut.Enable = 'on'; + n = length(file_picks); + omega = 1:n; + index = zeros(1,n); + index(value + 1) = omega(value); + index(setdiff(omega,value + 1)) = omega(setdiff(omega,value)); + file_picks = file_picks(index); + full_file_picks = full_file_picks(index); + dir_picks = dir_picks(index); + value = value + 1; + if show_full_path + set(pickslist,'String',full_file_picks,'Value',value) + else + set(pickslist,'String',file_picks,'Value',value) + end + if max(value) == n + movedownbut.Enable = 'off'; + end + moveupbut.Enable = 'on'; + end + + function togglefilter(varargin) + fig.Pointer = 'watch'; + drawnow + value = showallfiles.Value; + if value + filter = '*'; + re_filter = ''; + set([filter_ed,refilter_ed],'Enable','off') + else + filter = filter_ed.String; + re_filter = refilter_ed.String; + set([filter_ed,refilter_ed],'Enable','on') + end + full_filter = fullfile(current_dir,filter); + fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,sort_state,c)); + filenames = {fdir.name}'; + [filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + set(navlist,'String',filenames,'Value',[]) + addbut.Enable = 'off'; + fig.Pointer = 'arrow'; + end + + function toggle_refiltdirs(varargin) + fig.Pointer = 'watch'; + drawnow + value = refilterdirs.Value; + prop.redirs = value; + full_filter = fullfile(current_dir,filter); + fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,sort_state,c)); + filenames = {fdir.name}'; + [filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + set(navlist,'String',filenames,'Value',[]) + addbut.Enable = 'off'; + fig.Pointer = 'arrow'; + end + + function setfilspec(varargin) + fig.Pointer = 'watch'; + drawnow + filter = filter_ed.String; + if isempty(filter) + filter = '*'; + filter_ed.String = filter; + end + % Process file spec if a subdirectory was included. + [p,f,e] = fileparts(filter); + if ~isempty(p) + newpath = fullfile(current_dir,p,''); + pathbox.String = newpath; + filter = [f,e]; + if isempty(filter) + filter = '*'; + end + filter_ed.String = filter; + change_path(); + end + full_filter = fullfile(current_dir,filter); + fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,sort_state,c)); + filenames = {fdir.name}'; + [filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + set(navlist,'String',filenames,'Value',[]) + addbut.Enable = 'off'; + fig.Pointer = 'arrow'; + end + + function setrefilter(varargin) + fig.Pointer = 'watch'; + drawnow + re_filter = refilter_ed.String; + fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,sort_state,c)); + filenames = {fdir.name}'; + [filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + set(navlist,'String',filenames,'Value',[]) + addbut.Enable = 'off'; + fig.Pointer = 'arrow'; + end + + function filter_type_callback(varargin) + type_value = type_popup.Value; + set(filter_ed,'String',prop.type{type_value,1}) + setfilspec() + end + + function done(varargin) + % Optional shortcut: click on a file and press 'Done'. +% if isempty(full_file_picks) && strcmp(addbut.Enable,'on') +% add(); +% end + numfiles = length(full_file_picks); + if ~isempty(prop.numfiles) + if numfiles < prop.numfiles(1) + msg = {'Too few items selected.',numstr}; + uiwait(errordlg(msg,'','modal')) + return + elseif numfiles > prop.numfiles(end) + msg = {'Too many items selected.',numstr}; + uiwait(errordlg(msg,'','modal')) + return + end + end + fig_pos = fig.Position; + delete(fig) + end + + function cancel(varargin) + prop.output = 'cancel'; + fig_pos = fig.Position; + delete(fig) + end + + function history_cb(h,evt,arg) %#ok<INUSL> + fig.Pointer = 'watch'; + drawnow + if ischar(arg) + current_dir = arg; + else + current_dir = history(arg).name; + end +% history = update_history(history,current_dir,now,history_size); +% make_history_cm(csdata) + full_filter = fullfile(current_dir,filter); + path_cell = path2cell(current_dir); + fdir = filtered_dir(full_filter,re_filter,prop.redirs,... + @(x,c)file_sort(x,sort_state,c)); + filenames = {fdir.name}'; + [filenames,fdir] = annotate_file_names(filenames,fdir,fsdata); + set(dir_popup,'String',path_cell,'Value',length(path_cell)) + if length(path_cell) > 1 + up_dir_but.Enable = 'on'; + else + up_dir_but.Enable = 'off'; + end + pathbox.String = current_dir; + set(navlist,'ListboxTop',1,'Value',[],'String',filenames) + addbut.Enable = 'off'; + openbut.Enable = 'off'; + fig.Pointer = 'arrow'; + end + + function add_to_favorites(varargin) + favorites{end+1} = current_dir; + make_history_cm(csdata) + end + + function remove_from_favorites(varargin) + favorites = setdiff(favorites,current_dir); + make_history_cm(csdata) + end + + function clear_history(varargin) + history = update_history(history(1),'',[],history_size); + make_history_cm(csdata) + end + + function set_history_size(varargin) + result_cell = inputdlg('Number of Recent Folders:','',1,... + {sprintf('%g',history_size)}); + if isempty(result_cell) + return + end + result = sscanf(result_cell{1},'%f'); + if isempty(result) || result < 1 + return + end + history_size = result; + history = update_history(history,'',[],history_size); + make_history_cm(csdata) + setpref('uipickfiles','history_size',history_size) + end + + function resize(varargin) + % Get current figure size. + P = 'Position'; + pos = fig.Position; + w = pos(3); % figure width in pixels + h = pos(4); % figure height in pixels + + % Enforce minimum figure size. + w = max(w,564); + h = max(h,443); + if any(pos(3:4) < [w h]) + pos(3:4) = [w h]; + fig.Position = pos; + end + + % Change positions of all uicontrols based on the current figure + % width and height. + navw_pckw = round([1 1;-350 250]\[w-140;0]); + navw = navw_pckw(1); + pckw = navw_pckw(2); + navp = [10 10 navw h-174]; + pckp = [w-10-pckw 10 pckw h-174]; + set(navlist,P,navp) + set(pickslist,P,pckp) + + set(frame1,P,[navw+5 h-234 110 70]) + set(openbut,P,[navw+17 h-199 90 30]) + set(addbut,P,[navw+17 h-229 90 30]) + + frame2y = round((h-234 + 110 - 100)/2); + set(frame2,P,[w-pckw-115 frame2y 110 100]) + set(removebut,P,[w-pckw-105 frame2y+65 90 30]) + set(moveupbut,P,[w-pckw-105 frame2y+35 90 30]) + set(movedownbut,P,[w-pckw-105 frame2y+5 90 30]) + + set(done_button,P,[navw+30 80 80 30]) + set(cancel_button,P,[navw+30 30 80 30]) + + set(sort_cb(1),P,[15 h-163 70 15]) + set(sort_cb(2),P,[85 h-163 70 15]) + set(sort_cb(3),P,[155 h-163 70 15]) + + set(dir_popup,P,[10 h-144 navw-25 20]) + set(up_dir_but,P,[navw-10 h-144 20 20]) + set(pathbox,P,[10 h-119 navw 26]) + set(label1,P,[10 h-93 navw 16]) + + set(viewfullpath,P,[pckp(1) h-159 230 20]) + set(remove_dupes,P,[pckp(1) h-134 280 20]) + set(recall_button,P,[w-75 h-159 65 30]) + set(label4,P,[w-10-pckw h-89 pckw 20]) + set(num_files_warn,P,[w-10-pckw h-109 pckw 16]) + + label2.Position(2) = h - 18; + label3.Position(2) = h - 18; + showallfiles.Position(2) = h - 42; + refilterdirs.Position(2) = h - 64; + filter_ed.Position(2) = h - 44; + refilter_ed.Position(2) = h - 44; + set(type_popup,P,[10 h-72 250 20]) + end + + function reset_figure_size(varargin) + if strcmp(fig.SelectionType,'open') + root_units = get(groot,'Units'); + screen_size = get(groot,'ScreenSize'); + set(0,'Units',root_units) + hw = [740 494]; + pos = [round((screen_size(3:4) - hw - [0 26])/2),hw]; + fig.Position = pos; + resize() + end + end + + + +% ------------------ Other nested functions ------------------ + + function make_history_cm(csdata) + % Make context menu for history. + if ~isempty(hist_menus) + delete(hist_menus) + end + num_hist = length(history); + hist_menus = zeros(1,num_hist); + for i = 1:num_hist + hist_menus(i) = uimenu(hist_cm,'Label',history(i).name,... + 'Callback',{@history_cb,i}); + end + hist_menus(end+1) = uimenu(hist_cm,... + 'Label','Favorites',... + 'Enable','off',... + 'Separator','on'); + hist_menus(end+1) = uimenu(hist_cm,... + 'Label',[csdata.pre_home,csdata.home_folder,csdata.post],... + 'Callback',{@history_cb,csdata.home_folder}); + for i = 1:length(csdata.matlab_folders) + hist_menus(end+1) = uimenu(hist_cm,... + 'Label',[csdata.pre_logo,csdata.matlab_folders{i},csdata.post],... + 'Callback',{@history_cb,csdata.matlab_folders{i}}); %#ok<AGROW> + end + for i = 1:length(favorites) + hist_menus(end+1) = uimenu(hist_cm,... + 'Label',favorites{i},... + 'Callback',{@history_cb,favorites{i}}); %#ok<AGROW> + end + hist_menus(end+1) = uimenu(hist_cm,... + 'Label','Add Current Folder to Favorites',... + 'Separator','on',... + 'Callback',@add_to_favorites); + hist_menus(end+1) = uimenu(hist_cm,... + 'Label','Remove Current Folder from Favorites',... + 'Callback',@remove_from_favorites); + hist_menus(end+1) = uimenu(hist_cm,... + 'Label','Clear Menu',... + 'Separator','on',... + 'Callback',@clear_history); + hist_menus(end+1) = uimenu(hist_cm,'Label',... + sprintf('Set Number of Recent Folders (%d) ...',history_size),... + 'Callback',@set_history_size); + end + +end + + +% -------------------- Subfunctions -------------------- + +function [c,network_vol] = path2cell(p) +% Turns a path string into a cell array of path elements. +if ispc + p = strrep(p,'/','\'); + c1 = regexp(p,'(^\\\\[^\\]+\\[^\\]+)|(^[A-Za-z]+:)|[^\\]+','match'); + vol = c1{1}; + c = [{'My Computer'};c1(:)]; + if strncmp(vol,'\\',2) + network_vol = vol; + else + network_vol = ''; + end +else + c = textscan(p,'%s','delimiter','/'); + c = [{filesep};c{1}(2:end)]; + network_vol = ''; +end +end + +% -------------------- + +function p = cell2path(c) +% Turns a cell array of path elements into a path string. +if ispc + p = fullfile(c{2:end},''); + if p(end) == ':' + p = [p,filesep]; + end +else + p = fullfile(c{:},''); +end +end + +% -------------------- + +function d = filtered_dir(full_filter,re_filter,filter_both,sort_fcn) +% Like dir, but applies filters and sorting. +p = fileparts(full_filter); +if isempty(p) && full_filter(1) == '/' + p = '/'; +end +if fdexist(full_filter,'dir') + dfiles = repmat(dir(char(127)),0,1); +else + dfiles = dir(full_filter); +end +if ~isempty(dfiles) + dfiles([dfiles.isdir]) = []; +end + +ddir = dir(p); +ddir = ddir([ddir.isdir]); +[unused,index0] = sort(lower({ddir.name})); %#ok<ASGLU> +ddir = ddir(index0); +ddir(strcmp({ddir.name},'.') | strcmp({ddir.name},'..')) = []; + +% Additional regular expression filter. +if nargin > 1 && ~isempty(re_filter) + if ispc || ismac + no_match = cellfun('isempty',regexpi({dfiles.name},re_filter)); + else + no_match = cellfun('isempty',regexp({dfiles.name},re_filter)); + end + dfiles(no_match) = []; +end +if filter_both + if nargin > 1 && ~isempty(re_filter) + if ispc || ismac + no_match = cellfun('isempty',regexpi({ddir.name},re_filter)); + else + no_match = cellfun('isempty',regexp({ddir.name},re_filter)); + end + ddir(no_match) = []; + end +end +% Set navigator style: +% 1 => list all folders before all files, case-insensitive sorting +% 2 => mix files and folders, case-insensitive sorting +% 3 => list all files before all folders, case-insensitive sorting +% 4 => list all folders before all files, case-sensitive sorting +% 5 => mix files and folders, case-sensitive sorting +% 6 => list all files before all folders, case-sensitive sorting +nav_style = 1; +switch nav_style + case 1 + [unused,index1] = sort_fcn(dfiles,false); %#ok<ASGLU> + [unused,index2] = sort_fcn(ddir,false); %#ok<ASGLU> + d = [ddir(index2);dfiles(index1)]; + case 2 + d = [dfiles;ddir]; + [unused,index] = sort_fcn(d,false); %#ok<ASGLU> + d = d(index); + case 3 + [unused,index1] = sort_fcn(dfiles,false); %#ok<ASGLU> + [unused,index2] = sort_fcn(ddir,false); %#ok<ASGLU> + d = [dfiles(index1);ddir(index2)]; + case 4 + [unused,index1] = sort_fcn(dfiles,true); %#ok<ASGLU> + [unused,index2] = sort_fcn(ddir,true); %#ok<ASGLU> + d = [ddir(index2);dfiles(index1)]; + case 5 + d = [dfiles;ddir]; + [unused,index] = sort_fcn(d,true); %#ok<ASGLU> + d = d(index); + case 6 + [unused,index1] = sort_fcn(dfiles,true); %#ok<ASGLU> + [unused,index2] = sort_fcn(ddir,true); %#ok<ASGLU> + d = [dfiles(index1);ddir(index2)]; +end +end + +% -------------------- + +function [files_sorted,index] = file_sort(files,sort_state,casesen) +switch find(sort_state) + case 1 + if casesen + [files_sorted,index] = sort({files.name}); + else + [files_sorted,index] = sort(lower({files.name})); + end + if sort_state(1) < 0 + files_sorted = files_sorted(end:-1:1); + index = index(end:-1:1); + end + case 2 + if sort_state(2) > 0 + [files_sorted,index] = sort([files.datenum]); + else + [files_sorted,index] = sort([files.datenum],'descend'); + end + case 3 + if sort_state(3) > 0 + [files_sorted,index] = sort([files.bytes]); + else + [files_sorted,index] = sort([files.bytes],'descend'); + end +end +end + +% -------------------- + +function drives = getdrives(other_drives) +% Returns a cell array of drive names on Windows. +letters = char('A':'Z'); +num_letters = length(letters); +drives = cell(1,num_letters); +for i = 1:num_letters + if fdexist([letters(i),':\'],'dir') + drives{i} = [letters(i),':']; + end +end +drives(cellfun('isempty',drives)) = []; +if nargin > 0 && iscellstr(other_drives) + drives = [drives,unique(other_drives)]; +end +end + +% -------------------- + +function [filenames,dir_listing] = ... + annotate_file_names(filenames,dir_listing,fsdata) +% Adds a trailing filesep character to folder names and, optionally, +% prepends a folder icon or bullet symbol. +if ispc + for i = 1:length(filenames) + if ~isempty(regexpi(filenames{i},'\.lnk')) && ... + is_shortcut_to_dir(dir_listing(i).name) + filenames{i} = sprintf('%s%s%s%s',fsdata.pre_sc,filenames{i},... + fsdata.filesep,fsdata.post); + dir_listing(i).isdir = true; + elseif dir_listing(i).isdir + filenames{i} = sprintf('%s%s%s%s',fsdata.pre,filenames{i},... + fsdata.filesep,fsdata.post); + end + end +else + for i = 1:length(filenames) + if dir_listing(i).isdir + filenames{i} = sprintf('%s%s%s%s',fsdata.pre,filenames{i},... + fsdata.filesep,fsdata.post); + end + end +end +end + +% -------------------- + +function history = update_history(history,current_dir,time,history_size) +if ~isempty(current_dir) + % Insert or move current_dir to the top of the history. + % If current_dir already appears in the history list, delete it. + match = strcmp({history.name},current_dir); + history(match) = []; + % Prepend history with (current_dir,time). + history = [struct('name',current_dir,'time',time),history]; +end +% Trim history to keep at most <history_size> newest entries. +history = history(1:min(history_size,end)); +end + +% -------------------- + +function success = generate_folder_icon(icon_path) +% Black = 1, manila color = 2, transparent white = 3. +im = [ ... + 3 3 3 1 1 1 1 3 3 3 3 3; + 3 3 1 2 2 2 2 1 3 3 3 3; + 3 1 1 1 1 1 1 1 1 1 1 3; + 1 2 2 2 2 2 2 2 2 2 2 1; + 1 2 2 2 2 2 2 2 2 2 2 1; + 1 2 2 2 2 2 2 2 2 2 2 1; + 1 2 2 2 2 2 2 2 2 2 2 1; + 1 2 2 2 2 2 2 2 2 2 2 1; + 1 2 2 2 2 2 2 2 2 2 2 1; + 1 1 1 1 1 1 1 1 1 1 1 1]; +cmap = [0 0 0;255 220 130;255 255 255]/255; +fid = fopen(icon_path,'w'); +if fid > 0 + fclose(fid); + imwrite(im,cmap,icon_path,'Transparency',[1 1 0]) +end +success = fdexist(icon_path,'file'); +end + +% -------------------- + +function success = generate_foldersc_icon(icon_path) +% Black = 1, blue color = 2, darker blue = 3, transparent white = 4. +im = [ ... + 4 4 4 1 1 1 1 4 4 4 4 4; + 4 4 1 2 2 2 2 1 4 4 4 4; + 4 1 1 1 1 1 1 1 1 1 1 4; + 1 2 2 2 2 2 3 2 2 2 2 1; + 1 2 2 2 2 2 2 1 2 2 2 1; + 1 2 2 2 1 1 1 1 1 2 2 1; + 1 2 2 1 2 2 2 1 2 2 2 1; + 1 2 1 2 2 2 3 2 2 2 2 1; + 1 2 2 2 2 2 2 2 2 2 2 1; + 1 1 1 1 1 1 1 1 1 1 1 1]; +cmap = [0 0 0;163 185 255;65 83 128;255 255 255]/255; +fid = fopen(icon_path,'w'); +if fid > 0 + fclose(fid); + imwrite(im,cmap,icon_path,'Transparency',[1 1 1 0]) +end +success = fdexist(icon_path,'file'); +end + +% -------------------- + +function success = generate_house_icon(icon_path) +im = [6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6; + 6 6 6 6 6 6 6 5 5 6 6 6 6 6 6 6; + 6 6 6 6 6 6 5 8 8 5 6 8 8 8 6 6; + 6 6 6 6 6 5 8 3 3 8 5 2 9 2 6 6; + 6 6 6 6 5 8 3 3 3 3 8 5 9 2 6 6; + 6 6 6 5 8 3 7 4 4 7 3 8 5 2 6 6; + 6 6 5 8 3 3 1 1 1 1 3 3 8 5 6 6; + 6 5 8 3 3 3 2 4 4 2 3 3 3 8 5 6; + 5 8 2 3 3 3 3 3 3 3 3 3 3 2 8 5; + 6 6 2 3 3 3 1 1 1 1 3 3 3 2 6 6; + 6 6 2 3 3 3 1 8 8 1 3 3 3 2 6 6; + 6 6 2 3 3 3 1 8 8 1 3 3 3 2 6 6; + 6 6 2 3 3 3 1 1 2 1 3 3 3 2 6 6; + 6 6 2 3 3 3 1 5 5 1 3 3 3 2 6 6; + 6 6 2 3 3 3 1 7 7 1 3 3 3 2 6 6; + 6 6 8 8 8 8 4 4 4 4 8 8 8 8 6 6]; +im = [im,6*ones(16,5)]; +cmap = [70 24 9;174 172 166;244 240 230;93 96 97;32 33 33;... + 255 255 255;153 71 21;66 52 39;255 255 254]/255; +fid = fopen(icon_path,'w'); +if fid > 0 + fclose(fid); + imwrite(im,cmap,icon_path,'Transparency',[1 1 1 1 1 0 1 1 1]) +end +success = fdexist(icon_path,'file'); +end + +% -------------------- + +function success = generate_logo_icon(icon_path) +im = [9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9; + 9 9 9 9 9 9 9 9 9 9 10 9 9 9 9 9; + 9 9 9 9 9 9 9 9 9 10 8 6 9 9 9 9; + 9 9 9 9 9 9 9 9 9 4 3 7 10 9 9 9; + 9 9 9 9 9 9 9 9 2 1 7 7 6 9 9 9; + 9 9 9 9 9 9 9 2 1 1 7 7 3 9 9 9; + 9 9 9 9 9 10 4 1 1 8 7 7 3 5 9 9; + 9 9 9 9 10 1 1 1 1 8 7 7 3 6 5 9; + 9 9 10 2 4 4 1 1 8 8 7 7 3 3 9 9; + 10 2 2 2 4 4 1 1 8 3 3 7 7 3 6 9; + 9 2 2 4 4 4 1 8 8 3 7 7 7 3 6 5; + 9 9 10 2 4 1 8 8 8 3 7 7 6 6 3 5; + 9 9 9 9 9 1 8 3 3 7 7 9 9 9 6 6; + 9 9 9 9 9 5 3 7 7 7 5 9 9 9 9 5; + 9 9 9 9 9 9 6 7 7 5 9 9 9 9 9 9; + 9 9 9 9 9 9 5 6 5 9 9 9 9 9 9 9]; +im = [im,9*ones(16,5)]; +cmap = [73 50 49;132 193 188;182 60 15;97 146 141;246 224 205;... + 223 153 109;230 113 15;123 33 18;255 255 255;202 212 210]/255; +fid = fopen(icon_path,'w'); +if fid > 0 + fclose(fid); + imwrite(im,cmap,icon_path,'Transparency',[1 1 1 1 1 1 1 1 0 1]) +end +success = fdexist(icon_path,'file'); +end + +% -------------------- + +function fsdata = set_folder_style(folder_style_pref) +% Set style to preference. +fsdata.style = folder_style_pref; +% If style = 1, check to make sure icon image file exists. If it doesn't, +% try to create it. If that fails set style = 2. +if fsdata.style == 1 + icon1_path = fullfile(prefdir,'uipickfiles_folder_icon.png'); + icon2_path = fullfile(prefdir,'uipickfiles_foldersc_icon.png'); + if ~(fdexist(icon1_path,'file') && fdexist(icon2_path,'file')) + success1 = generate_folder_icon(icon1_path); + success2 = generate_foldersc_icon(icon2_path); + if ~(success1 && success2) + fsdata.style = 2; + end + end +end +% Set pre and post fields. +if fsdata.style == 1 + icon1_url = ['file:///',strrep(strrep(icon1_path,':','|'),'\','/')]; + icon2_url = ['file:///',strrep(strrep(icon2_path,':','|'),'\','/')]; + fsdata.pre = sprintf('<html><img width=12 height=10 src="%s"> ',icon1_url); + fsdata.pre_sc = sprintf('<html><img width=12 height=10 src="%s"> ',icon2_url); + fsdata.post = '</html>'; +elseif fsdata.style == 2 + fsdata.pre = '<html><b>•</b> '; + fsdata.pre_sc = '<html><b>•</b> '; + fsdata.post = '</html>'; +elseif fsdata.style == 3 + fsdata.pre = ''; + fsdata.pre_sc = ''; + fsdata.post = ''; +end +fsdata.filesep = filesep; + +end + +% -------------------- + +function csdata = set_cmenu_style(cmenu_style_pref) +% Set style to preference. +csdata.style = cmenu_style_pref; +% If style = 1, check to make sure icon image files exist. If they don't, +% try to create them. If that fails set style = 2. +if csdata.style == 1 + icon1_path = fullfile(prefdir,'uipickfiles_home_icon.png'); + icon2_path = fullfile(prefdir,'uipickfiles_logo_icon.png'); + if ~(fdexist(icon1_path,'file') && fdexist(icon2_path,'file')) + success1 = generate_house_icon(icon1_path); + success2 = generate_logo_icon(icon2_path); + if ~(success1 && success2) + csdata.style = 2; + end + end +end +% Set pre and post fields. +if csdata.style == 1 + icon1_url = ['file:///',strrep(strrep(icon1_path,':','|'),'\','/')]; + icon2_url = ['file:///',strrep(strrep(icon2_path,':','|'),'\','/')]; + csdata.pre_home = sprintf('<html><img width=21 height=16 src="%s">',icon1_url); + csdata.pre_logo = sprintf('<html><img width=21 height=16 src="%s">',icon2_url); + csdata.post = '</html>'; +elseif csdata.style == 2 + csdata.pre_home = ''; + csdata.pre_logo = ''; + csdata.post = ''; +end + +% Get MATLAB folders from userpath. +matlab_folders = regexp(userpath,pathsep,'split'); +matlab_folders(cellfun(@isempty,matlab_folders)) = []; +if ispc + csdata.home_folder = getenv('USERPROFILE'); +else + csdata.home_folder = getenv('HOME'); +end +csdata.matlab_folders = matlab_folders; + +end + +% -------------------- + +function prop = parsepropval(prop,varargin) +% Parse property/value pairs and return a structure. +properties = fieldnames(prop); +arg_index = 1; +while arg_index <= length(varargin) + arg = varargin{arg_index}; + if ischar(arg) + prop_index = match_property(arg,properties); + prop.(properties{prop_index}) = varargin{arg_index + 1}; + arg_index = arg_index + 2; + elseif isstruct(arg) + arg_fn = fieldnames(arg); + for i = 1:length(arg_fn) + prop_index = match_property(arg_fn{i},properties); + prop.(properties{prop_index}) = arg.(arg_fn{i}); + end + arg_index = arg_index + 1; + else + error(['Properties must be specified by property/value pairs',... + ' or structures.']) + end +end +end + +% -------------------- + +function prop_index = match_property(arg,properties) +% Utility function for parsepropval. +prop_index = find(strcmpi(arg,properties)); +if isempty(prop_index) + prop_index = find(strncmpi(arg,properties,length(arg))); +end +if length(prop_index) ~= 1 + error('Property ''%s'' does not exist or is ambiguous.',arg) +end +end + +% -------------------- + +function r = fdexist(item_path,type) +%fdexist: Check if file or directory exists. Does not search MATLAB path. +% type must be 'dir' or 'file'. +if strncmpi(type,'dir',length(type)) + r = java.io.File(item_path).isDirectory(); +elseif strncmpi(type,'file',length(type)) + r = java.io.File(item_path).isFile(); +end +end + +% -------------------- + +function r = is_shortcut_to_dir(filename) +r = false; +% jFile = java.io.File(filename); +% sf = sun.awt.shell.ShellFolder.getShellFolder(jFile); +% r = sf.isDirectory(); +end + +% -------------------- + +function d = subdirs(basedir,depth,exclusions) +%subdirs: Recursive directory finder. +% Recursively find all subdirectories of a specified directory. +% +% Syntax: +% dirs = subdirs; +% +% will return all subdirectories of the current directory, including the +% current directory, in a cell array of strings. +% +% dirs = subdirs(base); +% +% starts at the directory in the string, base, rather than the current +% directory. +% +% dirs = subdirs(base,depth); +% +% only searches to a limited depth (default is Inf). A depth of zero +% returns only the base directory, depth = 1 returns the base directory and +% its immediate decendants, etc. + +% Version: 1.1, 8 November 2014 +% Author: Douglas M. Schwarz +% Email: dmschwarz=ieee*org, dmschwarz=urgrad*rochester*edu +% Real_email = regexprep(Email,{'=','*'},{'@','.'}) + +% If base directory not specified, use current directory. +if nargin < 1 + basedir = pwd; +end + +% If depth not specified, search to infinite depth. +if nargin < 2 || isempty(depth) + depth = inf; +end + +if nargin < 3 || isempty(exclusions) + exclusions = ''; +end + +% If instructed not to search deeper, return basedir in a cell. +if depth == 0 + d = {basedir}; + return +end + +% Check exclusions for 'p' => exclude 'private' directories. Other +% exclusions characters exclude directories beginning with that character, +% e.g., '.@+'. +exclu = exclusions; +no_private = any(exclu == 'p'); +exclu(exclu == 'p') = []; + +% Get directory contents. +items = dir(basedir); + +% Get the name of each item in basedir. Remove items that are not +% directories and the special directories named '.' and '..', leaving only +% the desired subdirectories. +item_names = {items.name}; +item_names(~[items.isdir] | strcmp(item_names,'.') | ... + strcmp(item_names,'..')) = []; + +% Remove private directories, if desired. +if no_private + item_names(strcmpi(item_names,'private')) = []; +end + +% Remove directories beginning with the characters in exclu, e.g., '.@+'. +for i = 1:length(exclu) + item_names(strncmp(item_names,exclu(i),1)) = []; +end + +% Run this function recursively on each subdirectory and return basedir and +% those subdirectories. +num_items = length(item_names); +subitems = cell(1,num_items); +for i = 1:num_items + subitems{i} = subdirs(fullfile(basedir,item_names{i},''),... + depth - 1,exclusions); +end +d = [{basedir},subitems{:}]; +end