diff --git a/CASE1.m b/CASE1.m
index 51da101..bd754a1 100644
--- a/CASE1.m
+++ b/CASE1.m
@@ -1,7 +1,13 @@
function [efficiency, VR, P_r, pf] = CASE1(A, B, C, D)
%CASE1 Summary of this function goes here
-% Detailed explanation goes here
-V_r = complex((input('Recieving voltage (in kV): ') / sqrt(3)) * 10^3);
+% function measures line performance for 0.8 lagging power factor
+val = inputdlg("Recieving voltage (in kV): ");
+
+while isempty(str2num(val{1}))
+ val = inputdlg("Recieving voltage (in kV): ");
+end
+
+V_r = complex((str2num(val{1}) / sqrt(3)) * 10^3);
pf = 0.8; % 0.8 lagging power factor
phi = -1 * acos(pf);
P_r = 0:100*10^(3); % recieved active power
diff --git a/CASE2.m b/CASE2.m
index 41c9383..07b4aee 100644
--- a/CASE2.m
+++ b/CASE2.m
@@ -1,7 +1,13 @@
function [efficiency, VR, P_r, pf] = CASE2(A ,B ,C ,D)
-%UNTITLED2 Summary of this function goes here
+%CASE2 Summary of this function goes here
% calculate efficiency and voltage reg for case 2
-V_r = complex((input('Recieving voltage (in kV): ') / sqrt(3)) * 10^3);
+val = inputdlg("Recieving voltage (in kV): ");
+
+while isempty(str2num(val{1}))
+ val = inputdlg("Recieving voltage (in kV): ");
+end
+
+V_r = complex((str2num(val{1}) / sqrt(3)) * 10^3);
pf = 0.3:0.01:1;
phi_lag = -1 * acos(pf);
phi_lead = acos(pf);
@@ -34,8 +40,7 @@
VR_lag = ((abs(V_rnl_lag) - abs(V_r)) / abs(V_r)) * 100; % voltage regulation lag
VR_lead = ((abs(V_rnl_lead) - abs(V_r)) / abs(V_r)) * 100; % voltage regulation lead
-efficiency = [efficiency_lag efficiency_lead];
-VR = [VR_lag VR_lead];
-
+efficiency = [efficiency_lag; efficiency_lead];
+VR = [VR_lag; VR_lead];
end
diff --git a/TransmissionLines.prj b/TransmissionLines.prj
new file mode 100644
index 0000000..e019ce1
--- /dev/null
+++ b/TransmissionLines.prj
@@ -0,0 +1,132 @@
+
+
+ TransmissionLines
+
+
+ 1.0
+ Ahmed Osama
+ ahmed.osama8282@gmail.com
+
+
+
+
+
+ \TransmissionLines\
+ option.installpath.programfiles
+
+
+
+ ${PROJECT_ROOT}\TransmissionLines\for_testing
+ ${PROJECT_ROOT}\TransmissionLines\for_redistribution_files_only
+ ${PROJECT_ROOT}\TransmissionLines\for_redistribution
+ ${PROJECT_ROOT}\TransmissionLines
+ false
+
+ subtarget.standalone
+
+ true
+ false
+ false
+ MyAppInstaller_web
+ MyAppInstaller_mcr
+ MyAppInstaller_app
+ true
+ false
+
+ false
+ false
+
+ Syntax
+ -?
+
+ Input Arguments
+ -? print help on how to use the application
+ input arguments
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${PROJECT_ROOT}\app1_autoreflow2.mlapp
+
+
+
+
+ ${PROJECT_ROOT}\capacitance.m
+ ${PROJECT_ROOT}\CASE1.m
+ ${PROJECT_ROOT}\CASE2.m
+ ${PROJECT_ROOT}\inductance.m
+ ${PROJECT_ROOT}\inputsdlg.m
+ ${PROJECT_ROOT}\lineParameters.m
+ ${PROJECT_ROOT}\linePerformance.m
+ ${PROJECT_ROOT}\longLine.m
+ ${PROJECT_ROOT}\midiumLine.m
+ ${PROJECT_ROOT}\rcl.m
+ ${PROJECT_ROOT}\resistance.m
+ ${PROJECT_ROOT}\shortLine.m
+
+
+ D:\Engineering\College\Year 2\Second Term\Power Systems\Projects\Transmission-Lines\TransmissionLines\for_testing\TransmissionLines.exe
+ D:\Engineering\College\Year 2\Second Term\Power Systems\Projects\Transmission-Lines\TransmissionLines\for_testing\splash.png
+ D:\Engineering\College\Year 2\Second Term\Power Systems\Projects\Transmission-Lines\TransmissionLines\for_testing\readme.txt
+
+
+
+ C:\Program Files\Polyspace\R2021a
+
+
+
+
+
+ true
+
+
+
+
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+ 10.0
+ false
+ true
+ win64
+ true
+
+
+
\ No newline at end of file
diff --git a/TransmissionLines/PackagingLog.html b/TransmissionLines/PackagingLog.html
new file mode 100644
index 0000000..ffcd22d
--- /dev/null
+++ b/TransmissionLines/PackagingLog.html
@@ -0,0 +1,16 @@
+
+mcc -o TransmissionLines -W 'WinMain:TransmissionLines,version=1.0' -T link:exe -d 'D:\Engineering\College\Year 2\Second Term\Power Systems\Projects\Transmission-Lines\TransmissionLines\for_testing' -v 'D:\Engineering\College\Year 2\Second Term\Power Systems\Projects\Transmission-Lines\app1_autoreflow2.mlapp' -r 'C:\Program Files\Polyspace\R2021a\toolbox\compiler\resources\default_icon.ico'
+Compiler version: 8.2 (R2021a)
+
+Analyzing file dependencies.
+
+Parsing file "D:\Engineering\College\Year 2\Second Term\Power Systems\Projects\Transmission-Lines\app1_autoreflow2.mlapp"
+ (referenced from command line).
+Generating file "D:\Engineering\College\Year 2\Second Term\Power Systems\Projects\Transmission-Lines\TransmissionLines\for_testing\readme.txt".
+Packaging...
+Creating the bundle...
+Creating the install agent URL file...
+Web based installer created at D:\Engineering\College\Year 2\Second Term\Power Systems\Projects\Transmission-Lines\TransmissionLines\for_redistribution\MyAppInstaller_web.exe.
+Packaging complete.
+Elapsed packaging time was: 7 seconds.
+
diff --git a/TransmissionLines/for_redistribution/MyAppInstaller_web.exe b/TransmissionLines/for_redistribution/MyAppInstaller_web.exe
new file mode 100644
index 0000000..987643f
Binary files /dev/null and b/TransmissionLines/for_redistribution/MyAppInstaller_web.exe differ
diff --git a/TransmissionLines/for_redistribution_files_only/TransmissionLines.exe b/TransmissionLines/for_redistribution_files_only/TransmissionLines.exe
new file mode 100644
index 0000000..d393ac0
Binary files /dev/null and b/TransmissionLines/for_redistribution_files_only/TransmissionLines.exe differ
diff --git a/TransmissionLines/for_redistribution_files_only/readme.txt b/TransmissionLines/for_redistribution_files_only/readme.txt
new file mode 100644
index 0000000..2352a8e
--- /dev/null
+++ b/TransmissionLines/for_redistribution_files_only/readme.txt
@@ -0,0 +1,47 @@
+TransmissionLines Executable
+
+1. Prerequisites for Deployment
+
+Verify that version 9.10 (R2021a) of the MATLAB Runtime is installed.
+If not, you can run the MATLAB Runtime installer.
+To find its location, enter
+
+ >>mcrinstaller
+
+at the MATLAB prompt.
+NOTE: You will need administrator rights to run the MATLAB Runtime installer.
+
+Alternatively, download and install the Windows version of the MATLAB Runtime for R2021a
+from the following link on the MathWorks website:
+
+ https://www.mathworks.com/products/compiler/mcr/index.html
+
+For more information about the MATLAB Runtime and the MATLAB Runtime installer, see
+"Distribute Applications" in the MATLAB Compiler documentation
+in the MathWorks Documentation Center.
+
+2. Files to Deploy and Package
+
+Files to Package for Standalone
+================================
+-TransmissionLines.exe
+-MCRInstaller.exe
+ Note: if end users are unable to download the MATLAB Runtime using the
+ instructions in the previous section, include it when building your
+ component by clicking the "Runtime included in package" link in the
+ Deployment Tool.
+-This readme file
+
+
+
+3. Definitions
+
+For information on deployment terminology, go to
+https://www.mathworks.com/help and select MATLAB Compiler >
+Getting Started > About Application Deployment >
+Deployment Product Terms in the MathWorks Documentation
+Center.
+
+
+
+
diff --git a/TransmissionLines/for_redistribution_files_only/splash.png b/TransmissionLines/for_redistribution_files_only/splash.png
new file mode 100644
index 0000000..d60c6fa
Binary files /dev/null and b/TransmissionLines/for_redistribution_files_only/splash.png differ
diff --git a/TransmissionLines/for_testing/TransmissionLines.exe b/TransmissionLines/for_testing/TransmissionLines.exe
new file mode 100644
index 0000000..d393ac0
Binary files /dev/null and b/TransmissionLines/for_testing/TransmissionLines.exe differ
diff --git a/TransmissionLines/for_testing/mccExcludedFiles.log b/TransmissionLines/for_testing/mccExcludedFiles.log
new file mode 100644
index 0000000..9d0f909
--- /dev/null
+++ b/TransmissionLines/for_testing/mccExcludedFiles.log
@@ -0,0 +1,2 @@
+The List of Excluded Files
+Excluded files Exclusion Message ID Reason For Exclusion Exclusion Rule
diff --git a/TransmissionLines/for_testing/readme.txt b/TransmissionLines/for_testing/readme.txt
new file mode 100644
index 0000000..2352a8e
--- /dev/null
+++ b/TransmissionLines/for_testing/readme.txt
@@ -0,0 +1,47 @@
+TransmissionLines Executable
+
+1. Prerequisites for Deployment
+
+Verify that version 9.10 (R2021a) of the MATLAB Runtime is installed.
+If not, you can run the MATLAB Runtime installer.
+To find its location, enter
+
+ >>mcrinstaller
+
+at the MATLAB prompt.
+NOTE: You will need administrator rights to run the MATLAB Runtime installer.
+
+Alternatively, download and install the Windows version of the MATLAB Runtime for R2021a
+from the following link on the MathWorks website:
+
+ https://www.mathworks.com/products/compiler/mcr/index.html
+
+For more information about the MATLAB Runtime and the MATLAB Runtime installer, see
+"Distribute Applications" in the MATLAB Compiler documentation
+in the MathWorks Documentation Center.
+
+2. Files to Deploy and Package
+
+Files to Package for Standalone
+================================
+-TransmissionLines.exe
+-MCRInstaller.exe
+ Note: if end users are unable to download the MATLAB Runtime using the
+ instructions in the previous section, include it when building your
+ component by clicking the "Runtime included in package" link in the
+ Deployment Tool.
+-This readme file
+
+
+
+3. Definitions
+
+For information on deployment terminology, go to
+https://www.mathworks.com/help and select MATLAB Compiler >
+Getting Started > About Application Deployment >
+Deployment Product Terms in the MathWorks Documentation
+Center.
+
+
+
+
diff --git a/TransmissionLines/for_testing/requiredMCRProducts.txt b/TransmissionLines/for_testing/requiredMCRProducts.txt
new file mode 100644
index 0000000..3ed6af0
--- /dev/null
+++ b/TransmissionLines/for_testing/requiredMCRProducts.txt
@@ -0,0 +1 @@
+35000 35010
\ No newline at end of file
diff --git a/TransmissionLines/for_testing/splash.png b/TransmissionLines/for_testing/splash.png
new file mode 100644
index 0000000..d60c6fa
Binary files /dev/null and b/TransmissionLines/for_testing/splash.png differ
diff --git a/TransmissionLines/for_testing/unresolvedSymbols.txt b/TransmissionLines/for_testing/unresolvedSymbols.txt
new file mode 100644
index 0000000..7c2fc16
--- /dev/null
+++ b/TransmissionLines/for_testing/unresolvedSymbols.txt
@@ -0,0 +1 @@
+Path Symbol Reason
diff --git a/app1_autoreflow2.mlapp b/app1_autoreflow2.mlapp
new file mode 100644
index 0000000..13dd950
Binary files /dev/null and b/app1_autoreflow2.mlapp differ
diff --git a/inputsdlg.m b/inputsdlg.m
new file mode 100644
index 0000000..cdd512c
--- /dev/null
+++ b/inputsdlg.m
@@ -0,0 +1,3199 @@
+function [Answer,Canceled] = inputsdlg(Prompt, Title, Formats, DefAns, Options)
+%INPUTSDLG Enhanced input dialog box supporting multiple data types
+% ANSWER = INPUTSDLG(PROMPT) creates a modal dialog box that returns user
+% input for multiple prompts in the cell array ANSWER. PROMPT is a 1-D
+% cell array containing the PROMPT strings.
+%
+% Alternatively, PROMPT can have up to 4 columns. The first column
+% sppecifies the prompt string. The second column to specify the struct
+% field names to output ANSWER as a structure. The third column specifies
+% units (i.e., post-fix labels to the right of controls) to display. The
+% fourth column specifies the tooltip string. The tooltip string is ignored
+% for text type.
+%
+% INPUTSDLG uses UIWAIT to suspend execution until the user responds.
+%
+% ANSWER = INPUTSDLG(PROMPT,NAME) specifies the title for the dialog.
+%
+% Note that INPUTSDLG(PROMPT) & INPUTSDLG(PROMPT,NAME) are similar to the
+% standard INPUTDLG function, except for the dialog layout.
+%
+% ANSWER = INPUTSDLG(PROMPT,NAME,FORMATS) can be used to specify the type
+% of parameters to display with FORMATS matrix of structures. The dimension
+% of FORMATS defines how PROMPT items are laid out in the dialog box. For
+% example, if PROMPT has 6 elements and the size of FORMATS is 2x3 then,
+% the items are shown in 2 rows, 3 columns format. The items in PROMPT
+% correspond to a horizontal traversal of FORMATS.
+%
+% The fields in FORMATS structure are:
+%
+% type - Type of control ['check',{'edit'},'list','range','text',
+% 'color','table','button','none']
+% style - UI control type used. One of:
+% {'checkbox'}, for 'check' type
+% {'edit'} for 'edit' type
+% {'listbox','popupmenu','radiobutton','togglebutton'}
+% for 'list' type
+% {'slider'} for 'range' type
+% {'text'} for 'text' type
+% {'edit'} for 'color' type
+% {'pushbutton'} for 'button' and 'color' types
+% {'table'} for 'table' type
+% format - Data format: ['text','date','float','integer','logical',
+% 'vector','file','dir']
+% limits - [min max] (see below for details)
+% required - 'on' - control must have an answer
+% {'off'} - control may return empty answer
+% items - Type 'edit', Format 'file': File flter spec
+% Type 'list': Selection items (cell of strings)
+% Type 'table': Names of columns (cell of strings)
+% Type 'range': Slider step size spec [minor major]
+% size - [width height] in pixels. Set to <=0 to auto-size.
+% enable - Defines how to respond to mouse button clicks, including which
+% callback routines execute. One of:
+% {'on'} - UI control is operational.
+% 'inactive' ?UI control is not operational, but looks the
+% same as when Enable is on.
+% 'off' ?UI uicontrol is not operational and its image
+% is grayed out.
+% margin - A scalar or 2-element vector specifying the margin between control
+% and its labels in pixels.
+% labelloc - Prompt label location:
+% {'lefttop'} - left of control, aligned to top
+% 'leftmiddle' - left of control, aligned to middle
+% 'leftbottom' - left of control, aligned to bottom
+% 'topleft' - above control, aligned left
+% 'topcenter' - above control, aligned center
+% 'topright' - above control, aligned right
+% unitsloc - Units label location:
+% {'righttop'} - right of control, aligned to top
+% 'rightmiddle' - right of control, aligned to middle
+% 'rightbottom' - right of control, aligned to bottom
+% 'bottomleft' - below control, aligned left
+% 'bottomcenter' - below control, aligned center
+% 'bottomright' - below control, aligned right
+% callback - Defines callback funcion, a routine that executes whenever
+% you activate the uicontrol object. For the controls with
+% separate dialog, their callback functions are executed after
+% the dialog is closed. The callback function must be given as
+% a function handle with following syntax:
+%
+% my_callbackfcn(hobj,evt,handles,k)
+%
+% where hobj and evt are the passed-through standard MATLAB
+% callback arguments, handles is a Nx3 array of dialog
+% objects. Here, the n-th row corresponds to the n-th PROMPT,
+% and handles(n,1) is the calling object handle (i.e., same as
+% hobj). handles(n,2) are the prompt texts and handles(n,3)
+% are the prompt unit texts.
+%
+% For example, Formats(n,m).callback.ButtonDownFcn sets the
+% the button-press callback function.
+% span - Defines size of objects in fields [rows columns]
+%
+% A missing field (either missing from FORMATS struct or the field value is
+% left empty for an element) will be filled with a default field value.
+%
+% FORMATS type field defines what type of prompt item to be shown.
+%
+% type Description
+% -------------------------------------------------------------------
+% edit Standard edit box (single or multi-line mode)
+% check Check box for boolean item
+% list Chose from a list of items ('listbox' style allows multiple item
+% selection)
+% range Use slider to chose a value over a range
+% text Static text (e.g., for instructions)
+% color Color selection using 'uisetcolor'
+% button Execute function defined in 'callback'
+% table Uitable
+% none A placeholder. May be used for its neighboring item to extend
+% over multiple columns or rows (i.e., "to merge cells")
+%
+% The allowed data format depends on the type of the field:
+%
+% type allowed format
+% --------------------------------------------
+% check {logical}, integer, text
+% edit {text}, date, float, integer, file, dir, vector
+% list {integer}, text
+% range {float}
+% color {float}, integer
+% table (a cell string to specify ColumnFormat of uiTable)
+% button any data format allowed
+%
+% Formats 'file' and 'dir' for 'edit' type uses the standard UIGETFILE,
+% UIPUTFILE, and UIGETDIR functions to retrieve a file or directory name.
+%
+% The role of limits field varies depending on other parameters:
+%
+% style role of limits
+% ---------------------------------------------------
+% checkbox If data format is integer, limits(1) is the ANSWER value
+% if the check box is not selected box is not selected and
+% limits(2) is the ANSWER if the check box is selected. If
+% data format is text, its limits field must be given as a
+% cellstring array.
+% edit:text
+% If diff(limits)>0, text aligns with the prompt label. If
+% diff(limits)<0, tet aligns with the control.
+% edit::date
+% limits must be a free-format date format string or a
+% scalar value specifying the date format. Supported format
+% numbers are: 0,1,2,6,13,14,15,16,23. The default date
+% format is 2 ('mm/dd/yy'). See the tables in DATESTR help
+% for the format definitions. As long as the user entry is
+% a valid date/time expression, the dialog box
+% automatically converts to the assigned format.
+% edit::float, edit::integer
+% This style defines the range of allowed values if numeric
+% vector is given, including the values specified. For
+% other cases, use cell array to customize its
+% validateattribute call. For example, {'positive','<',1}
+% to specify value between 0 and 1, excluding 0 & 1.
+% edit::vector
+% limits specifies the allowed number of elements in a
+% vector. Use cell array to customize its validateattribute
+% call if vector length is fixed or completely arbitrary.
+% Note that 'column' attribute is always forced unless
+% 'row' attribute is explicitly specified.
+% edit::file
+% If 01 uses
+% UIGETFILE in multi-select mode with multi-line edit. If
+% diff(limits)<=0 usees UIPUTFILE with single-line edit
+% list::listbox If diff(limits)>1, multiple items can be selected. If
+% auto-height and limits(1)>0, at most limits(1) lines will
+% be shown.
+% slider limits(1) defines the smallest value while
+% limits(2) defines the largest value
+% color 'float' format limit must be [0 1] and 'integer' format
+% limit must be [0 255]. If not, the behavior is not
+% determined.
+% table cell array defining ColumnWidths
+% none If diff(limits)==0 space is left empty (default)
+% If diff(limits)>0 : lets the item from left to extend
+% If diff(limits)<0 : lets the item from above to extend
+% NOTE: Use 'span' field of a control to automatically set
+% the extension mode.
+%
+% Similar to how PROMPT strings are laid out, when FORMATS.style is set to
+% either 'radiobutton' or 'togglebutton', FORMATS.items are laid out
+% according to the dimension of FORMATS.items.
+%
+% There are two quick format options as well:
+%
+% Quick Format Option 1 (mimicing INPUTDLG behavior):
+% FORMATS can specify the number of lines for each edit-type prompt in
+% FORMATS. FORMATS may be a constant value or a column vector having
+% one element per PROMPT that specifies how many lines per input field.
+% FORMATS may also be a matrix where the first column specifies how
+% many rows for the input field and the second column specifies how
+% many columns wide the input field should be.
+%
+% Quick Format Option 2:
+% FORMATS can specify the types of controls and use their default
+% configurations. This option, however, cannot be used to specify
+% 'list' control as its items are not specified. To use this option,
+% provide a string (if only 1 control) or a cell array of strings. If
+% a cell array is given, its dimension is used for the dialog
+% layout.
+%
+% ANSWER = INPUTSDLG(PROMPT,NAME,FORMATS,DEFAULTANSWER) specifies the
+% default answer to display for each PROMPT. For a non-tiled layout,
+% DEFAULTANSWER must contain the same number of elements as PROMPT (that
+% are not of 'none' style). If PROMPT does not provide ANSWER structure
+% fields, DEFAULTANSWER should be a cell array with element type
+% corresponding to FORMATS.format. Leave the cell element empty for a
+% prompt with 'text' type. If ANSWER is a structure, DEFAULTANSWER must be
+% a struct with the specified fields. (If additional fields are present in
+% DEFAULTANSWER, they will be returned as parts of ANSWER.)
+%
+% For edit::file controls, a default answer that does not correspond to an
+% existing file will be used as a default path and/or file name in the
+% browse window. It is passed as the DefaultName parameter to UIGETFILE or
+% UIPUTFILE.
+%
+% To enable Tiled Mode, FORMATS must be given as a vector and DEFAULTANSWER
+% must be given as a cell matrix or a struct vector. If FORMATS is a row
+% vector, the dialog controls are tiled vertically; conversely if it is a
+% column vector, the controls are tiled horizontally. If DEFAULTANSWER is
+% given as a cell matrix, the number of rows of DEFAULTANSWER must match
+% the number of elements of PROMPT. Each column of DEFAULTANSWER forms a
+% tile row/column. If DEFAULTANSWER is given as a struct vector, each
+% struct element forms a tile row/column.
+%
+% Empty rows and empty columns are automatically eliminated by default. To
+% add an empty row or column, set the FORMATS entry of one of its cells to
+% type = 'none' with its size = [H W] or [-Hmin -Wmin] specified to the
+% desired width and height.
+%
+% ANSWER = INPUTSDLG(PROMPT,NAME,FORMATS,DEFAULTANSWER,OPTIONS) specifies
+% additional options. If OPTIONS is the string 'on', the dialog is made
+% resizable. If OPTIONS is a structure, the fields recognized are:
+%
+% Option Field Description {} indicates the default value
+% ----------------------------------------------------------------------
+% Resize Make dialog resizable: 'on' | {'off'}
+% WindowStyle Sets dialog window style: {'normal'} | 'modal'
+% Interpreter Label text interpreter: 'latex' | {'tex'} | 'none'
+% CancelButton Show Cancel button: {'on'} | 'off'
+% ApplyButton Adds Apply button: 'on' | {'off'}
+% Sep Space b/w prompts in pixels: {10}
+% ButtonNames Customize OK|Cancel|Apply button names: {up to 3 elements}
+% AlignControls Align adjacent controls in the same column: 'on' | {'off'}
+% FontSize Customize font size. Default: get(0,'DefaultUicontrolFontSize')
+% CreateFcn Callback function executed right after dialog creation
+% with syntax my_createfcn(hobj,evt,handles) with
+% standard MATLAB callback arguments, hobj & evt, and Nx3
+% array of handles. Here, the n-th row corresponds to the
+% n-th PROMPT, and handles(n,1) is the calling object handle
+% (i.e., same as hobj). handles(n,2) are the prompt texts
+% and handles(n,3) are the prompt unit texts.
+% DeleteFcn Callback function executed just before deleting dialog.
+% The function syntax is the same as CreateFcn.
+%
+% [ANSWER,CANCELED] = INPUTSDLG(...) returns CANCELED = TRUE if user
+% pressed Cancel button, closed the dialog, or pressed ESC. In such event,
+% the content of ANSWER is set to the default values.
+%
+% Note on Apply Button feature. Pressing the Apply button makes the current
+% change permanent. That is, pressing Cancel button after pressing Apply
+% button only reverts ANSWER back to the states when the Apply button was
+% pressed last. Also, if user pressed Apply button, CANCELED flag will not
+% be set even if user canceled out of the dialog box.
+%
+% Examples:
+%
+% prompt={'Enter the matrix size for x^2:';'Enter the colormap name:'};
+% name='Input for Peaks function';
+% formats(1) = struct('type','edit','format','integer','limits',[1 inf]);
+% formats(2) = struct('type','edit','format','text','limits',[0 1]);
+% defaultanswer={20,'hsv'};
+%
+% [answer,canceled] = inputsdlg(prompt,name,formats,defaultanswer);
+%
+% formats(2).size = -1; % auto-expand width and auto-set height
+% options.Resize='on';
+% options.WindowStyle='normal';
+% options.Interpreter='tex';
+%
+% answer = inputsdlg(prompt,name,formats,defaultanswer,options);
+%
+% prompt(:,2) = {'Ndim';'Cmap'};
+% defaultanswer = struct(defaultanswer,prompt(:,2),1);
+%
+% answer = inputsdlg(prompt,name,formats,defaultanswer,options);
+%
+% See also INPUTDLG, DIALOG, ERRORDLG, HELPDLG, LISTDLG, MSGBOX,
+% QUESTDLG, UIGETFILE, UIPUTFILE, UIGETDIR, DATESTR, VALIDATEATTRIBUTE.
+
+% Version 2.2.0 (June 25, 2015)
+% Written by: Takeshi Ikuma
+% Contributors: Andreas Greuer, Luke Reisner, Florian Hatz
+% Created: Nov. 16, 2009
+% Revision History:
+% v.1.1 (Nov. 19, 2009)
+% * Fixed bugs (reported by AG):
+% - not returning Canceled output
+% - erroneous struct output behavior
+% - error if all row elements of a column are auto-expandable
+% * Added Apply button option
+% * Added support for Units (label to the right of controls)
+% * Updated the help text
+% v.1.11 (Nov. 20, 2009)
+% * Fixed bugs (reported by AG):
+% - incorrect Canceled output when Cancel button is pressed
+% v.1.12 (Nov. 20, 2009)
+% * Fixed bugs (reported by AG):
+% - again incorrect Canceled output behavior
+% v.1.2 (May 20, 2010)
+% * Fixed bugs (reported by AG & Jason):
+% - Apply button->Canel button does not revert back to post-apply answers.
+% - Line 265 handles.Figure -> handles.fig
+% * Added edit::date support
+% * Added formats.enable support
+% * Added options.CancelButton support
+% * Added options.ButtonNames support
+% v.1.2.1 (June 11, 2010)
+% * Fixed default option bug (reported by Jason)
+% v.1.2.2 (July 15, 2010)
+% * Rewritten checkoptions() (to correct issues reported by Jason)
+% * Bug Fix: file & dir control enable config were interpreted backwards
+% v.1.2.3 (July 19, 2010)
+% * checkoptions() bug fix (to correct issues reported by Kevin)
+% v.1.3 (August 13, 2010, by Luke Reisner)
+% * Improved dialog layout:
+% - Less wasted space, better control distribution, more consistent margins
+% - Buttons are right-aligned per OS standards
+% * Changed edit::date to return a simple date vector (see DATEVEC help)
+% * Added support for free-form date format specifiers to edit::date
+% * Added ability to limit the number of displayed lines for a listbox
+% * Added ability to set default browse path/filename for edit::file controls
+% * Added options.AlignControls to align adjacent controls in the same column
+% * Added options.UnitsMargin to control spacing between controls and units
+% * Fixed bugs:
+% - Flickering or misplaced controls when dialog first appears
+% - Radiobutton and togglebutton controls couldn't be disabled
+% - Edit::integer controls allowed non-integer values
+% - Slider controls didn't auto-size properly
+% - Other minor miscellaneous bugs
+% v.2.0 (July 17, 2013, by T. Ikuma & F. Hatz)
+% * PROMPT(:,4) to specify tooltip strings
+% * Enabled Tiled Mode with DEFAULTANSWER & FORMATS dimension specs. See
+% inputsdlg_demo_struct
+% * Added types: table, color, button
+% * Added formats: logical and vector
+% * Added 'text' format for 'list' type
+% * Added 'callback' format field
+% * Added 'CreateFcn' option field
+% * Added 'DeleteFcn' option field
+% * Added 'required' format field
+% * Added 'labelloc' and 'unitsloc' format fields to customize location of
+% the labels
+% * removed UnitMargin option field and added 'margin' format field.
+% * Added 'span' format field to make spanning across multiple rows and
+% columns simpler
+% * Improved handling of 'Formats' (new variable 'span' / see inputsdlg_demo)
+% * A dialog with non-editable text only displays 'OK' button and does not
+% return any argument
+% * edit:file: diff(formats.limits)==0 => uiputfile
+% v.2.0.1 (Aug 05, 2013, by T. Ikuma)
+% * Bug fix on inputdlg compatible calls
+% v.2.0.2 (Aug 06, 2013, by T. Ikuma)
+% * Version compatibility fix for pre-R2013a
+% v.2.0.3 (Sep 07, 2013)
+% * Bug fix on parsing popup menu callback (reported by E. Morales)
+% v.2.0.4 (Nov 25, 2013)
+% * Bug fix on parsing empty callback format field (reported by David v.B.)
+% v.2.0.5 (Mar 28, 2014)
+% * Improved error message on FORMATS-PROMPT size mismatch.
+% * Fixed several bugs relating to neglected ToolTipString support
+% (thanks David v.B!)
+% * FORMATS.items for Slider style now sets uicontrol's SliderStep
+% property
+% v.2.0.6 (Jul 01, 2014)
+% * bug fix in the text positioning (~Line 2350)
+% * bug fix in check_formats to insert type='edit' as default
+% v.2.1.0 (Jul 03, 2014)
+% * Added support for 'none' Format type with positive size to act as a
+% placeholder for a row or column.
+% * Removed restriction on Cancel & Apply buttons when there are only text
+% controls present.
+% * Bug fix on setting default answer for list:radiobutton and
+% list:togglebutton
+% * Bug fix on ANSWER reporting non-empty default answer for
+% list:radiobutton and list:togglebutton with required='off'.
+% * Bug fix on multi-select file formats
+% v.2.1.1 (Sept 17, 2014)
+% * Improved backward compatibility
+% * Bug fix in setting default value for list:integer:radiobutton control
+% v.2.1.2 (Sept 19, 2014)
+% * Improved check_formats routine (faster and better backward
+% compatibility)
+% v.2.2 (June 25, 2015)
+% * Added logical:text control with its limits format field specifying the
+% returning string values
+% * Numeric editboxes uses validateattribute to check its value. Its
+% formats.limits field may now be set to cell array to specify the
+% attribute
+% * Fixed errorneous behavior when pressed Enter key in Edit control
+% containing invalid value
+% v.2.3 (June 26, 2015)
+% * Added the row vector option for edit:vector via specifying 'row' in
+% the cell-array limits definition
+% * Bug fix in edit:vector limits parser
+% v.2.3.1 (June 29, 2015)
+% * Bug fixes in default value processing
+% v.2.3.2 (June 30, 2015)
+% * Bug fix for list:text default value processing (case insensitive)
+
+% to-do list
+% * Add edit:font for the built-in uisetfont dialog
+% * Support for DefaultXXX option fields to set default Formats field values
+% * Auto-layout given figure's desired width
+% * Limits-based option for positioning of text-type
+% * Better spanning support
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% # of argument Check %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+try
+ narginchk(0,5);
+ nargoutchk(0,2);
+catch
+ error(nargchk(0,5,nargin)); %#ok
+ error(nargchk(0,2,nargout)); %#ok
+end
+%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Handle Input Args %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%
+if nargin<1, Prompt={}; end
+if nargin<2, Title = ''; end
+if nargin<3, Formats=struct([]); end
+if nargin<4, DefAns = {}; end
+if nargin<5, Options = struct([]); end
+
+% Check Prompt input
+[Prompt,FieldNames,Units,TooltipStrings,err] = checkprompt(Prompt);
+if ~isempty(err), error(err{:}); end
+NumQuest = numel(Prompt); % number of prompts
+
+if isempty(Title)
+ Title = ' ';
+elseif iscellstr(Title)
+ Title = Title{1}; % take the first entry
+elseif ~ischar(Title)
+ error('inputsdlg:InvalidInput','Title must be a string of cell string.');
+end
+
+% make sure that the Options is valid
+[Options,FormatDefaultFields,err] = checkoptions(Options);
+if ~isempty(err), error(err{:}); end
+
+% make sure that the Formats structure is valid & fill it in default values
+% as needed
+[Formats,err] = checkformats(Formats,NumQuest,FormatDefaultFields);
+if ~isempty(err), error(err{:}); end
+
+% make sure that the DefAns is valid & set Answer using DefAns and default
+% values if DefAns not given
+[DefaultAnswer,TileMode,err] = checkdefaults(DefAns,Formats,FieldNames);
+if ~isempty(err), error(err{:}); end
+Answer = DefaultAnswer;
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Create Dialog GUI %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%
+% lay contents out on a dialog box
+[Formats,handles,sinfo] ...
+ = buildgui(Title,Prompt,Units,TooltipStrings,FieldNames,Formats,TileMode,Options);
+
+% fill in the default answers, setup callbacks,
+initgui();
+
+IsRequired = arrayfun(@(fmt)strcmp(fmt.required,'on'),Formats);
+DefaultReqMet = checkReq(DefaultAnswer);
+ReqMet = false; % also modified by the Apply button
+
+ReturnPress = false;
+Applied = false; % set true by pressing Apply Button
+Canceled = ~ishghandle(handles.fig);
+
+% Go into uiwait if the figure handle is still valid.
+% This is mostly the case during regular use.
+try
+ while ~(Canceled || ReqMet) % exit only if Canceled or all the rerequired fields are filled
+
+ % Wait till uiresume is called
+ uiwait(handles.fig);
+
+ figflag = get(handles.fig,'UserData');
+
+ % if Return key press released from uiwait, make sure associated
+ % callback did not cause an error
+ if ReturnPress
+ ReturnPress = false;
+ if strcmp(figflag,'Error')
+ ReqMet = false;
+ set(handles.fig,'UserData','');
+ continue;
+ end
+ end
+
+ % Check handle validity again since figure could be deleted externally
+ Canceled = strcmp(figflag,'Cancel');
+
+ if Canceled % return the default answer
+ Answer = DefaultAnswer; % revert back to the default answer
+ else
+ Answer = getAnswer(); % get the final answers
+ ReqMet = checkReq(Answer);
+ if ~ReqMet
+ h = errordlg('All required parameters must be filled.','Missing Required Value(s)','modal');
+ uiwait(h);
+ end
+ end
+ end
+
+ % if user deletefcn defined, call it now
+ if ~isempty(Options.DeleteFcn)
+ Options.DeleteFcn(handles.fig,[],handles.ctrls);
+ end
+
+ % Close the figure if it's still open
+ delete(handles.fig);
+
+catch ME
+ if ishghandle(handles.fig)
+ delete(handles.fig);
+ ME.getReport
+ throw(ME);
+ else
+ error('Inputsdlg dialog window was closed externally');
+ end
+end
+
+% If Canceled, convert Canceled to integer depending on Applied condition
+Canceled = Canceled * (Canceled + (Applied && DefaultReqMet));
+% 0 - OK pressed
+% 1 - Canceled
+% 2 - Canceled but prior to it Applied button has been pressed, filled in
+% all the required answers
+
+% If Tiled, reshape the answer
+Answer = reshape(Answer,NumQuest,max(TileMode));
+
+% If FieldNames given, convert Answer to struct
+Answer = selectivecell2struct(Answer,FieldNames);
+
+% If dialog contains only non-editable texts, return w/o output argument
+if nargout==0 && all(sinfo.istext)
+ clear Answer
+end
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% NESTED FUNCTIONS
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ function initgui
+
+ fig = handles.fig;
+
+ % set OK button as the default
+ fh = handle(fig);
+ fh.setDefaultButton(handles.btns(1));
+
+ % Set callbacks of figure/control buttons
+ set(fig, 'UserData', 'Cancel',...
+ 'WindowKeyPressFcn', @doFigureKeyPress,...
+ 'ResizeFcn', @(~,~)resizegui(handles,sinfo,Options),...
+ 'CloseRequestFcn', @(hObj,evd)doOKCancel(hObj,evd,false));
+ for k = 1 : numel(handles.btns)
+ hbtn = handles.btns(k);
+ switch get(hbtn, 'UserData')
+ case 'OK'
+ set(hbtn, 'Callback', @(hObj,evd)doOKCancel(hObj,evd,true));
+ case 'Cancel'
+ set(hbtn, 'Callback', @(hObj,evd)doOKCancel(hObj,evd,false));
+ case 'Apply'
+ set(hbtn, 'Callback', @doApply);
+ end
+ end
+
+ % Set callback functions and set default values
+ for k = 1:numel(Answer)
+
+ h = handles.ctrls(k,1);
+ val = Answer{k};
+ fmt = Formats(k);
+ cbnames = fieldnames(fmt.callback);
+
+ ena = {'Enable',fmt.enable};
+ fcnname = 'Callback';
+ fcn = {};
+ aux = {};
+
+ % get callback function handles for custom callbacks to be
+ % called from inputsdlg's default callback
+ [tf,I] = ismember('callback',lower(cbnames));
+ if tf
+ cbfcn = fmt.callback.(cbnames{I});
+ else
+ cbfcn = {};
+ end
+ [tf,I] = ismember('buttondownfcn',lower(cbnames));
+ if tf
+ bdfcn = fmt.callback.(cbnames{I});
+ else
+ bdfcn = {};
+ end
+
+ switch fmt.style
+ case 'checkbox'
+ ansname = 'Value';
+ if strcmp(fmt.format,'text')
+ val = strcmp(val,fmt.limits{2});
+ end
+ case 'edit'
+ ansname = 'String';
+ switch fmt.format
+ case {'integer','float'}
+ % for numeric edit box, check for the range & set mouse down behavior
+ fcn = @(hObj,evd)checkNumericRange(hObj,evd,k,cbfcn);
+ %aux = {'UserData',val}; % save the numeric data
+ val = num2str(val);
+ case 'date'
+ fcn = @(hObj,evd)checkDate(hObj,evd,k,cbfcn);
+ case 'vector'
+ % for vector edit box, check for the range & set mouse down behavior
+ fcn = @(hObj,evd)checkVector(hObj,evd,k,cbfcn);
+
+ val = num2str(val);
+ case 'file'
+ mode = diff(fmt.limits);
+ fcnname = 'ButtonDownFcn';
+ if strcmp(ena{2},'on')
+ ena{2} = 'inactive';
+ fcn = @(hObj,evd)openFilePrompt(hObj,evd,k,bdfcn);
+ end
+
+ val = cellstr(val);
+ if ~isempty(val)
+ dirname = fileparts(val{1});
+ if ~isdir(dirname), dirname = ''; end
+ else
+ dirname = '';
+ end
+ aux = {'UserData',dirname};
+
+ if mode <= 1 % single-file
+ val = val{1};
+ end
+ case 'dir'
+ fcnname = 'ButtonDownFcn';
+ if strcmp(ena{2},'on')
+ ena{2} = 'inactive';
+ fcn = @(hObj,evd)openDirPrompt(hObj,evd,k,bdfcn);
+ end
+ end
+ case 'pushbutton'
+ if strcmp(fmt.type,'color')
+ if strcmp(ena{2},'on')
+ fcn = @(hObj,evd)openColorPrompt(hObj,evd,k,bdfcn);
+ end
+ ansname = 'BackgroundColor';
+ else
+ ansname = 'UserData';
+ end
+ case {'radiobutton', 'togglebutton'}
+ fcnname = 'SelectionChangeFcn';
+ ansname = 'SelectedObject';
+ hbtn = get(h,'UserData'); % hButtons
+ if strcmp(fmt.format,'integer')
+ val = hbtn(val);
+ else
+ val = findobj(hbtn,'flat','String',val);
+ end
+ set(hbtn,ena{:});
+ ena = {};
+ case 'table'
+ ansname = 'Data';
+ fcnname = 'CellEditCallback';
+ case 'slider'
+ if strcmp(fmt.format,'integer')
+ fcn = @(hObj,evd)forceInteger(hObj,evd,k,cbfcn);
+ end
+ ansname = 'Value';
+ case {'listbox' 'popupmenu'}
+ ansname = 'Value';
+ end
+
+ % Set control's properties
+ if ~strcmp(fmt.type,'text')
+ set(h,ena{:},fcnname,fcn,ansname,val,aux{:});
+
+ % Set custom callbacks
+ if ~isempty(fmt.callback)
+ for n = 1:numel(cbnames)
+ % set if the specfied callback function not already assigned
+ % as the part of INPUTSDLG functionality.
+ if ~strcmpi(cbnames{n},fcnname) || isempty(fcn)
+ cbfcn = @(hobj,evt)fmt.callback.(cbnames{n})(hobj,evt,handles.ctrls,k);
+ try
+ set(h,cbnames{n},cbfcn);
+ catch
+ error('Invalid callback function name.');
+ end
+ end
+ end
+ end
+ end
+ end
+
+ % make sure we are on screen
+ movegui(handles.fig)
+
+ % if there is a figure out there and it's modal, we need to be modal too
+ if ~isempty(gcbf) && strcmp(get(gcbf,'WindowStyle'),'modal')
+ set(handles.fig,'WindowStyle','modal');
+ end
+
+ % if user createfcn defined, call it now
+ if ~isempty(Options.CreateFcn)
+ Options.CreateFcn(handles.fig,[],handles.ctrls);
+ end
+
+ set(handles.fig,'Visible','on');
+ drawnow;
+
+ % set focus on the first uicontol
+ h = findobj(handles.ctrls(:,1),'flat','-not','Style','text');
+ if ~isempty(h)
+ h = h(1);
+ switch get(h,'type')
+ case 'uicontrol', uicontrol(h);
+ case {'uipanel' 'uibuttongroup'}
+ hsel = get(h,'SelectedObject');
+ if isempty(hsel), hsel = get(h,'Children'); hsel(1:end-1) = []; end
+ uicontrol(hsel);
+ case 'uitable', uitable(h);
+ end
+ end
+ end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ function answer = getAnswer()
+
+ answer = cell(size(Answer));
+
+ % retrieve answer from controls
+ for i = 1:numel(answer)
+
+ h = handles.ctrls(i,1);
+ fmt = Formats(i);
+
+ switch fmt.style
+ case 'checkbox'
+ val = get(h,'Value');
+
+ if ~isempty(val)
+ switch fmt.format
+ case 'text' % 'popupmenu' 'listbox'
+ answer(i) = fmt.limits(val+1);
+ case 'logical' % 'checkbox'
+ answer{i} = val==get(h,'Max');
+ otherwise %{'float' 'integer'}
+ answer{i} = val;
+ end
+ end
+ case {'popupmenu' 'listbox' 'slider'}
+ val = get(h,'Value');
+
+ if ~isempty(val)
+ switch fmt.format
+ case 'text' % 'popupmenu' 'listbox'
+ str = get(h,'String');
+ answer{i} = str(val);
+ case 'logical' % 'checkbox'
+ answer{i} = val==get(h,'Max');
+ otherwise %{'float' 'integer'}
+ answer{i} = val;
+ end
+ end
+ case 'edit'
+ str = get(h,'String');
+ switch fmt.format
+ case {'float','integer'}
+ if ~isempty(str)
+ answer{i} = str2double(str);
+ else
+ answer{i} = [];
+ end
+ case 'date'
+ if isempty(str)
+ answer{i} = []; % Return an empty date vector if no date was entered
+ else
+ answer{i} = datevec(str, fmt.limits);
+ end
+ case 'file'
+ if diff(fmt.limits)>1
+ answer{i} = cellstr(str);
+ else
+ answer{i} = str;
+ end
+ case {'vector'}
+ answer{i} = str2num(str); %#ok
+ otherwise %case {'text' 'dir'}
+ answer{i} = str;
+ end
+ case {'radiobutton' 'togglebutton'} % uibuttongroup
+ hbtn = get(h,'SelectedObject');
+ if isempty(hbtn) && strcmp(fmt.required,'on')
+ disp('required but not given')
+ end
+ if strcmp(fmt.format,'text')
+ if isempty(hbtn)
+ answer{i} = '';
+ else
+ answer{i} = get(hbtn,'String');
+ end
+ else
+ if isempty(hbtn)
+ answer{i} = [];
+ else
+ answer{i} = find(hbtn==get(h,'UserData'));
+ end
+ end
+ case 'pushbutton'
+ if strcmp(fmt.type,'color')
+ answer{i} = get(h,'BackgroundColor');
+ if strcmp(fmt.format,'integer')
+ answer{i} = uint8(round(answer{i}*255));
+ end
+ else
+ answer{i} = get(h,'UserData');
+ end
+ case 'table'
+ answer{i} = get(h,'Data');
+ end
+ end
+ end
+
+ function reqmet = checkReq(answer)
+ idx = cellfun(@isempty,answer);
+ reqmet = ~any(IsRequired(:)&idx(:));
+ end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Control Button Callback callback functions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ function doFigureKeyPress(obj, evd)
+ [tf,I] = ismember(evd.Key,{'return','space','escape'});
+ if ~tf, return; end % nothing special to do
+
+ ReturnPress = false;
+ if any(I==[1 2])
+
+ % Ignore under a condition when GCO is an edit uicontrol
+ % Known Potential Issue: GCO could be modified from outside
+ hedit = findobj(gco,'flat','type','uicontrol','style','edit');
+
+ ignore = ~isempty(hedit);
+ if ignore
+ % check for the conditions
+ if I==1 % return
+ % resume only if not currently in a multi-line edit uicontrol
+ ignore = get(hedit,'Max')-get(hedit,'Min')>1;
+ ReturnPress = true;
+ end
+
+ if ignore
+ return;
+ end
+ end
+
+ % equivalent to pressing OK button
+ set(obj,'UserData','OK');
+ elseif I==3 % Cancel
+ % equivalent to pressing Cancel button
+ set(obj,'UserData','Cancel');
+ else
+ return;
+ end
+
+ % set focus on OK button (to update the value of the current control)
+ uicontrol(handles.btns(1));
+
+ % if reached this far, valid key press to close the dialog
+ uiresume(obj);
+
+ end
+
+ function doOKCancel(~, ~, isok)
+ if isok
+ set(gcbf,'UserData','OK');
+ uiresume(gcbf);
+ else
+ set(gcbf,'UserData','Cancel');
+ uiresume(gcbf); % cancel
+ end
+ end
+
+ function doApply(~,~)
+ DefaultAnswer = getAnswer(); % retrieve the current answers from the controls
+ DefaultReqMet = checkReq(DefaultAnswer);
+ Applied = true;
+ end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% UICONTROL ButtonDownFcn callback functions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ function checkNumericRange(hObj,evt,k,cbfcn)
+
+ fmt = Formats(k);
+ errored = false;
+
+ str = get(hObj,'String');
+ if isempty(str)
+ if fmt.required
+ % show an error dialog
+ h = errordlg('This parameter must be filled with a value.','Required Value','modal');
+ errored = true;
+ else
+ Answer{k} = [];
+ end
+ else
+
+ % convert to float/integer
+ val = str2double(str);
+ isint = strcmp(fmt.format, 'integer');
+ if isint
+ val = round(val); % Round to the nearest integer
+ end
+
+ % validate the value
+ lim = fmt.limits;
+ if ~iscell(lim)
+ lim = {'' lim(1) '' lim(2)};
+ if lim{4}==inf
+ lim([3 4]) = [];
+ else
+ lim{3} = '<=';
+ end
+ if lim{2}==-inf
+ lim([1 2]) = [];
+ else
+ lim{1} = '>=';
+ end
+ end
+
+ try
+ validateattributes(val,{'numeric'},[{'scalar'} lim]);
+ % Re-format the control's text according to the value
+ set(hObj, 'String', num2str(val));
+ Answer{k} = val; % store the numeric answer to revert to later
+ catch MExcept
+ h = errordlg(MExcept.message,'Invalid Value','modal');
+ errored = true;
+ end
+ end
+
+
+ if errored
+ % set flag
+ set(handles.fig,'UserData','Error');
+
+ % revert back to the previous value
+ set(hObj,'String',num2str(Answer{k}));
+ uiwait(h);
+ elseif ~isempty(cbfcn) % success
+ % run custom callback function
+ cbfcn(hObj,evt,handles.ctrls,k);
+ end
+
+ end
+
+ function checkVector(hObj,evd,k,cbfcn)
+
+ fmt = Formats(k);
+ errored = false;
+
+ str = get(hObj,'String');
+ if isempty(str)
+ if fmt.required
+ % show an error dialog
+ h = errordlg('This parameter must be filled with a value.','Required Value','modal');
+ errored = true;
+ else
+ Answer{k} = [];
+ end
+ else
+ % convert to float/integer
+ val = str2num(str); %#ok
+
+ lim = fmt.limits;
+
+ try
+ if iscell(lim)
+ if any(strcmpi(lim,'row'))
+ type = {};
+ else
+ type = {'column'};
+ end
+
+ validateattributes(val,{'numeric'},[type lim]);
+ else
+ N = numel(val);
+ if Nlim(2) % incompatible dimension
+ error('This vector parameter must have %d to %d elements.',lim(1),lim(2));
+ end
+ end
+
+ % Re-format the control's text according to the value
+ Answer{k} = val; % force to be a column vector and store to revert to later
+ set(hObj, 'String', num2str(Answer{k}));
+ catch MExcept
+ h = errordlg(MExcept.message,'Invalid Value','modal');
+ errored = true;
+ end
+ end
+
+ if errored
+ % set flag
+ set(handles.fig,'UserData','Error');
+
+ % revert back to the previous value
+ set(hObj,'String',num2str(Answer{k}));
+ uiwait(h);
+ elseif ~isempty(cbfcn) % success
+ % run custom callback function
+ cbfcn(hObj,evd,handles.ctrls,k);
+ end
+
+ end
+
+ function checkDate(hObj,evd,k,cbfcn)
+
+ format = Formats(k).limits;
+ errored = false;
+
+ str = get(hObj,'string');
+ if isempty(str) % Avoid calling datenum() which prints a warning for empty strings
+ if fmt.required
+ % show an error dialog
+ h = errordlg('This parameter must be filled with a value.','Required Value','modal');
+ errored = true;
+ else
+ Answer{k} = '';
+ end
+ else
+ try
+ num = datenum(str, format); % Check if the input matches the custom date format first
+ catch
+ try
+ num = datenum(str); % Check if the input matches any other supported date format
+ catch
+ h = errordlg(sprintf('Unsupported date format.'),'Invalid Value','modal');
+ errored = true;
+ end
+ end
+ Answer{k} = datestr(num,format);
+ set(hObj,'String',Answer{k});
+ end
+
+ if errored
+ % set flag
+ set(handles.fig,'UserData','Error');
+
+ % revert back to the previous value
+ set(hObj,'String',num2str(Answer{k}));
+ uiwait(h);
+ elseif ~isempty(cbfcn)
+ % run custom callback function
+ cbfcn(hObj,evd,handles.ctrls,k);
+ end
+
+ end
+
+ function openFilePrompt(hObj,evd,k,cbfcn)
+ fmt = Formats(k);
+ spec = fmt.items;
+ opt = {};
+ mode = diff(fmt.limits);
+ filename = get(hObj,'String');
+ if mode<=0 % uiputfile
+ uifilefcn = @uiputfile;
+ file = filename;
+ else
+ uifilefcn = @uigetfile;
+ if mode>1 % multi-select
+ file = get(hObj,'UserData');
+ opt = {'MultiSelect','on'};
+ else
+ file = filename;
+ end
+ end
+
+ % open the file prompt
+ [f,p] = uifilefcn(spec,'',file,opt{:});
+ if ~(isequal(f,0) && isequal(p,0)) % canceled, no change
+
+ % store & display the data
+ file = fullfile(p,f); % form full path(es) to the selected files
+ set(hObj,'String',file);
+ if mode>1
+ set(hObj,'UserData',p);
+ end
+
+ % run custom callback function
+ if ~isempty(cbfcn)
+ cbfcn(hObj,evd,handles.ctrls,k);
+ end
+ end
+
+ % bring focus back to the control
+ uicontrol(hObj);
+ end
+
+ function openDirPrompt(hObj,evd,k,cbfcn)
+
+ p = uigetdir(get(hObj,'String'));
+ if ~isequal(p,0)
+ % display the selected directory
+ set(hObj,'String',p);
+
+ % run custom callback function
+ if ~isempty(cbfcn)
+ cbfcn(hObj,evd,handles.ctrls,k);
+ end
+ end
+
+ % bring focus back to the control
+ uicontrol(hObj);
+ end
+
+ function openColorPrompt(hObj,evd,k,cbfcn)
+
+ p = uisetcolor(get(hObj,'BackgroundColor'));
+ if ~isempty(p)
+ % display the selected color
+ set(hObj,'BackgroundColor',p);
+
+ % run custom callback function
+ if ~isempty(cbfcn)
+ cbfcn(hObj,evd,handles.ctrls,k);
+ end
+ end
+
+ % bring focus back to the control
+ uicontrol(hObj);
+ end
+
+ function forceInteger(hObj,evd,k,cbfcn)
+
+ % make sure the rounded value is within the allowed range
+ Answer{k} = max(get(hObj,'Min'),min(get(hObj,'Max'),round(get(hObj,'Value'))));
+
+ % set the rounded value
+ set(hObj,'Value',Answer{k});
+
+ % run custom callback function
+ if ~isempty(cbfcn)
+ cbfcn(hObj,evd,handles.ctrls,k);
+ end
+ end
+end
+
+function S = selectivecell2struct(C,fields)
+idx = ~cellfun(@isempty,fields);
+if any(idx)
+ idx = find(idx)';
+ S = cell2struct(C(idx,:),fields(idx),1);
+else
+ S = C;
+end
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% CHECKPROMPT :: Check Prompt input is valid & fill default values
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+function [Prompt,FieldNames,Units,TooltipStrings,err] = checkprompt(Prompt)
+
+% default configuration
+FieldNames = {}; % answer in a cell
+Units = {}; % no units
+TooltipStrings = {};
+
+% standard error
+err = {'inputsdlg:InvalidInput','Prompt must be a cell string with up to four columns.'};
+
+if isempty(Prompt), Prompt = {'Input:'};
+elseif ~iscell(Prompt), Prompt = cellstr(Prompt);
+end
+
+[nrow,ncol] = size(Prompt);
+
+% prompt given in a row -> transpose
+if ncol>4
+ if nrow<4, Prompt = Prompt.'; [nrow,ncol] = size(Prompt);
+ else return; % too many columns given
+ end
+end
+
+% struct fields defined
+if ncol>1
+ idx = cellfun(@isempty,Prompt(:,2));
+ FieldNames = Prompt(:,2);
+ FieldNames(idx) = {''}; % make sure it is empty cellstr
+
+ idx(:) = ~idx;
+ if numel(unique(FieldNames(idx)))~=sum(idx)
+ err{2} = 'Duplicate struct field name found.';
+ return;
+ end
+else
+ FieldNames = repmat({''},nrow,1);
+end
+
+% unit labels defined
+if ncol>2, Units = Prompt(:,3);
+else Units = repmat({''},nrow,1);
+end
+
+% tooltip strings defined
+if ncol>3, TooltipStrings = Prompt(:,4);
+else TooltipStrings = repmat({''},nrow,1);
+end
+
+% return only the labels in Prompt argument
+Prompt(:,2:end) = [];
+
+err = {}; % all cleared
+
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% CHECKFORMATS :: Check Formats input is valid & fill default values
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+function [Formats,err] = checkformats(Formats,NumQuest,fields)
+
+err = {};
+
+if isempty(Formats) % if Formats not defined, use the first entry (edit:text)
+ Formats = fields(1:2,:);
+ Formats(cellfun(@(c)iscell(c)&&isempty(c),Formats)) = {{{}}};
+ Formats = repmat(struct(Formats{:}),NumQuest,1);
+end
+
+fnames = lower(fields(1,:));
+fields(1,:) = [];
+nfields = numel(fnames); % sans the first row
+
+% backward compatibility (NumLines)
+if isnumeric(Formats)
+ [rw,cl]=size(Formats);
+ ok = rw==1;
+ if ok
+ OneVect = ones(NumQuest,1);
+ if cl == 2, NumLines=Formats(OneVect,:);
+ elseif cl == 1, NumLines=Formats(OneVect);
+ elseif cl == NumQuest, NumLines = Formats';
+ else ok = false;
+ end
+ end
+ if rw == NumQuest && any(cl == [1 2]), NumLines = Formats;
+ elseif ~ok
+ err = {'MATLAB:inputdlg:IncorrectSize', 'NumLines size is incorrect.'};
+ return;
+ end
+
+ % set to default edit control (column stacked)
+ fields(3:end,:) = []; % all to be edit boxes
+ Formats = repmat(struct(fields{:}),NumQuest,1);
+
+ % set limits according to NumLines(:,1)
+ numlines = mat2cell([zeros(NumQuest,1) NumLines(:,1)],ones(NumQuest,1),2);
+ [Formats.limits] = deal(numlines{:});
+
+ % sets the width to be 10*NumLines(:,2)
+ if (size(NumLines,2) == 2)
+ sizes = mat2cell([zeros(NumQuest,1) NumLines(:,2)],ones(NumQuest,1),2);
+ [Formats.size] = deal(sizes{:});
+ end
+
+ return;
+elseif ischar(Formats) || iscellstr(Formats) % given type
+ if ischar(Formats), Formats = cellstr(Formats); end
+ Formats = cell2struct(Formats,'type',3);
+elseif ~isstruct(Formats)
+ err = {'inputsdlg:InvalidInput','FORMATS must be an array of structure.'};
+ return
+end
+
+% Dialog grid dimension
+fdims = size(Formats);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% If span field is given, fill Format struct accordingly
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+if isfield(Formats,'span')
+
+ idx = ~arrayfun(@(s)isempty(s.span),Formats);
+ if ~arrayfun(@(s,sz)isnumeric(s.span) && ...
+ (numel(s.span)==2 && all(s.span==floor(s.span) & s.span>0)),Formats(idx))
+ err = {'inputsdlg:InvalidInput','FORMATS.span must be 2-element vectors of positive integers.'};
+ return;
+ end
+ if ~all(arrayfun(@(s)all(s.span<=fdims),Formats(idx)))
+ err = {'inputsdlg:InvalidInput','FORMATS.span extends the control out of the grid size specified by Formats matrix.'};
+ return;
+ end
+
+ [i,j] = find(arrayfun(@(s)any(size(s.span)>[1 1]),Formats));
+ for ind = [i j].' % for each spanning control
+ span = Formats(ind(1),ind(2)).span;
+ % extend from left on the top-most row
+ for col = ind(2)+(1:span(2)-1)
+ Formats(ind(1),col).type = 'none';
+ Formats(ind(1),col).limits = [0 1]; % extend from left
+ end
+ % extend from above for all other rows
+ for row = ind(1)+(1:span(1)-1)
+ for col = ind(2)+(0:span(2)-1)
+ Formats(row,col).type = 'none';
+ Formats(row,col).limits = [1 0]; % extend from above
+ end
+ end
+ end
+ Formats = rmfield(Formats,'span');
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% Convert Formats to cell
+[~,I] = ismember(lower(fieldnames(Formats)),fnames);
+if any(I==0)
+ err = {'inputsdlg:InvalidFormatsField','FORMATS contains invalid field name(s).'};
+ return
+end
+fvals = cell([prod(fdims) nfields]);
+fvals(:,I) = struct2cell(Formats(:)).';
+Nvals = numel(Formats);
+
+% Mark implicitly unused cells
+Iempty = cellfun(@isempty,fvals); % cell with empty format
+Iunk = all(Iempty,2); % if all type/format/style are all empty, cell format is completely unknown
+Inone = strcmp(fvals(:,1),'none'); % explictly specified empty cell
+Nmissing = NumQuest - (Nvals-sum(Iunk)-sum(Inone)); % number of unformated prompts
+if Nmissing<0
+ err = {'inputsdlg:InvalidInput',sprintf('%s\n%s',...
+ 'FORMATS must have matching number of elements to PROMPT (exluding ''none'' type).',...
+ 'If .span field is used, also check for overlapping controls.')};
+ return
+end
+if sum(Iunk)-Nmissing>0 % if extra unknown cells exist, set them to 'none'
+ Imorenone = find(Iunk,sum(Iunk)-Nmissing,'last');
+ fvals(Imorenone,1) = {'none'}; % set the rest of empty entries to none (spacer)
+end
+Iempty(:,1:2) = true; % to always copy the type & format columns
+
+noformat = cellfun(@isempty,fields(:,2));
+
+% avaiable styles for each Format type
+styles.text = {'text'};
+styles.check = {'checkbox'};
+styles.edit = {'edit'};
+styles.list = {'listbox','popupmenu','radiobutton','togglebutton'};
+styles.range = {'slider'};
+styles.table = {'table'};
+styles.color = {'pushbutton'};
+styles.button = {'pushbutton'};
+
+for n = 1:Nvals % for each format entry
+ [type,format,style] = deal(fvals{n,1:3});
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % Check type/format/style fields (Columns 1-2)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % if there are any field without type defined, set to default
+ if isempty(type) % type not specified
+ Ifield = true(size(fields,1),1); % can be any field
+ else % type specified
+ Ifield = strcmpi(type,fields(:,1));
+ if strcmpi(type,'table')
+ format = []; % ignore format field
+ Iempty(n,2) = false; % do not copy format field
+ end
+ end
+
+ if isempty(format) % format specified (ignore if style=table)
+ if isempty(type) && ~isempty(style) % only style specified
+ Ifield(:) = Ifield & strcmpi(fields(:,3),style);
+ end
+ else
+ Ifield(:) = Ifield & (noformat|strcmpi(fields(:,2),format));
+ end
+
+ % grab the first match
+ Ifield = find(Ifield,1,'first');
+ if isempty(Ifield)
+ [i,j] = ind2sub(fdims,n);
+ err = {'inputsdlg:InvalidInput',invalidformat_errormessage(i,j,type,format,fields(:,1:2))};
+ return;
+ end
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % Set all empty fields to type's defaults
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ fvals(n,Iempty(n,:)) = fields(Ifield,Iempty(n,:));
+ [type,format] = deal(fvals{n,1:2});
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % check style (Column 3)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ if ~Iempty(n,3)
+ try
+ fvals{n,3} = validatestring(style,styles.(type));
+ catch
+ err = {'inputsdlg:InvalidInput','Invalid FORMATS.style for ''range'' type must be ''slider''.'};
+ return
+ end
+ end
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % check items (Column 4)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % slider style: items specifies its SliderStep property
+ if ~Iempty(n,4)
+ items = fvals{n,4};
+ if strcmp(type,'range') % type = range
+ if ~any(cellfun(@(c)isnumeric(c)&&numel(c)==2&&all(c>0&c<1),items))
+ err = {'inputsdlg:InvalidInput','FORMATS.items for ''range'' type must be 2 element vector with values between 0 and 1.'};
+ return
+ end
+ elseif strcmp(type,'list')
+ % check items - convert if string array or numeric array given
+ if ischar(items)
+ fvals{n,4} = cellstr(items);
+ elseif isnumeric(items)
+ fvals{n,4} = num2cell(items);
+ elseif ~iscellstr(items)
+ err = {'inputsdlg:InvalidInput','FORMATS.items for ''list'' type must be either a cell of strings or of numbers.'};
+ return
+ end
+ end
+ end
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % check limits (Column 5)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ % edit::date - limits specifies the date display format
+ if ~Iempty(n,5)
+ lims = fvals{n,5};
+ if strcmp(type,'check') && strcmp(format,'text')
+ if ~(iscellstr(lims) && numel(lims)==2)
+ err = {'inputsdlg:InvalidInput','FORMATS.limits must be given as a two-element cellstring vector for ''check:text'' control.'};
+ end
+ elseif strcmp(type,'edit') && strcmp(format,'date')
+ if ischar(lims) % date format string given
+ try
+ datestr(1,lims);
+ catch
+ err = {'inputsdlg:InvalidInput', 'Invalid free-form format string in FORMATS.limits for ''date'' control.'};
+ return;
+ end
+ elseif ~(isnumeric(lims) && isscalar(lims) && any(lims==[0 1 2 6 13 14 15 16 23]))
+ err = {'inputsdlg:InvalidInput','FORMATS.limits for ''edit::date'' format must be one of 0,1,2,6,13,14,15,16,23.'};
+ return;
+ end
+ elseif strcmp(type,'table') % table - limits specifies the Column widths
+ if ~iscell(lims)
+ err = {'inputsdlg:InvalidInput','FORMATS.limits for ''table'' type must be given as a cell vector.'};
+ return;
+ end
+ elseif ~((isnumeric(lims) && numel(lims)==2)...
+ || (iscell(lims) && strcmp(type,'edit') && any(strcmp(format,{'float','integer','vector'}))))
+ err = {'inputsdlg:InvalidInput','FORMATS.limits must be given as a two-element vector.'};
+ return;
+ end
+ end
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % check size (Column 6)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ if ~Iempty(n,6)
+ sz = fvals{n,6};
+ if ~(isnumeric(sz) && any(numel(sz)==[1 2]) && ~any(isnan(sz)))
+ err = {'inputsdlg:InvalidInput','FORMATS.size must be 1 or 2 element non-NaN vector.'};
+ return
+ end
+
+ % if only scalar value given, set 0 as the second element
+ if isscalar(sz)
+ fvals{n,6}(2) = 0;
+ end
+ end
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % check enable (Column 7)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ if ~Iempty(n,7)
+ try
+ fvals{n,7} = validatestring(fvals{n,7},{'on','inactive','off'});
+ catch
+ err = {'inputsdlg:InvalidInput','FORMATS.enable must be one of {''on'',''inactive'',''off''}.'};
+ return;
+ end
+ end
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % check required (Column 8)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ if ~Iempty(n,8)
+ try
+ fvals{n,8} = validatestring(fvals{n,8},{'on','off'});
+ catch
+ err = {'inputsdlg:InvalidInput','FORMATS.required must be ''on'' or ''off''.'};
+ return;
+ end
+ end
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % check callback (Column 9)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ if ~Iempty(n,9)
+ cb = fvals{n,9};
+
+ if isstruct(cb) % given as struct, make sure all its elements are function handles
+ if all(structfun(@(cb)isa(cb,'function_handle'),cb))
+ err = {'inputsdlg:InvalidInput','FORMATS.callback must be given as a function handle or a struct containing function handles.'};
+ return;
+ end
+ elseif isa(cb,'function_handle') % convert to the struct form
+ if strcmp(type,'table')
+ fvals{n,9} = struct('CellEditCallback',cb);
+ elseif strcmp(type,'list') && any(strcmp(style,{'radiobutton','togglebutton'})) % uibuttongroup
+ fvals{n,9} = struct('SelectionChangeFcn',cb);
+ elseif strcmp(style,'edit') && (strcmp(type,'color') || any(strcmp(format,{'file','dir','font'}))) % inactive edit uicontrol
+ fvals{n,9} = struct('ButtonDownFcn',cb);
+ else
+ fvals{n,9} = struct('Callback',cb);
+ end
+ else
+ err = {'inputsdlg:InvalidInput','FORMATS.callback must be given as a function handle or a struct containing function handles.'};
+ return;
+ end
+ end
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % check prompt label location (Column 10)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ if ~Iempty(n,10)
+ try
+ fvals{n,10} = validatestring(fvals{n,10},{'lefttop','leftmiddle','leftbottom','topleft','topcenter','topright'});
+ catch
+ err = {'inputsdlg:InvalidInput','FORMATS.labelloc must be ''lefttop'', ''leftmiddle'', ''leftbottom'', ''topleft'', ''topcenter'', or ''topright''.'};
+ return;
+ end
+ end
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % check units label location (Column 11)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ if ~Iempty(n,11)
+ try
+ fvals{n,11} = validatestring(fvals{n,11},{'righttop','rightmiddle','rightbottom','bottomleft','bottomcenter','bottomright'});
+ catch
+ err = {'inputsdlg:InvalidInput','FORMATS.unitsloc must be ''righttop'', ''rightmiddle'', ''rightbottom'', ''bottomleft'', ''bottomcenter'', or ''bottomright''.'};
+ return;
+ end
+ end
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ % check label margins (Column 12)
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ if ~Iempty(n,12)
+ v = fvals{n,12};
+ if ~(isnumeric(v) && any(numel(v)==[1 2]) && ~any(isinf(v)|v<0))
+ err = {'inputsdlg:InvalidInput','FORMATS.margin must be 1 or 2 element positive vector.'};
+ return
+ end
+ if isscalar(v) % if scalar value given, use the same value for both margins
+ fvals{n,12}(2) = fvals{n,12};
+ end
+ end
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% gather back as Formats struct
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Formats = reshape(cell2struct(fvals,fnames,2),fdims);
+
+end
+
+function err = invalidformat_errormessage(i,j,type,format,fields)
+% return error message for specifying invalid cell format
+
+str = sprintf('Specified Format(%d,%d).',i,j);
+
+if isempty(type) % type not specified
+ Ifield = true(size(fields,1),1); % can be any field
+else % type specified
+ Ifield = strcmpi(type,fields(:,1));
+ if ~any(Ifield) % invalid type
+ types = unique(fields(:,1));
+ err = sprintf('%stype field value (''%s'') is unsupported.\n',str,type);
+ err = sprintf('%s\nSupported types are: %s',err,types{1});
+ for n = 2:numel(types)-1
+ err = sprintf('%s, %s',err,types{n});
+ end
+ if numel(types)>1
+ err = sprintf('%s, and %s.',err,types{end});
+ else
+ err = sprintf('%s.',err);
+ end
+ return;
+ end
+end
+
+I = strcmpi(fields(:,2),format);
+if ~any(Ifield&I)
+ formats = fields(Ifield,2);
+ str = sprintf('%sformat field value (''%s'') is unsupported',str,format);
+ if isempty(type)
+ err = sprintf('%s.',str);
+ else
+ err = sprintf('%s for %s cell type.',str,type);
+ end
+ err = sprintf('%s\nSupported formats are: %s',err,formats{1});
+ for n = 2:numel(formats)-1
+ err = sprintf('%s, %s',err,formats{n});
+ end
+ if numel(formats)>1
+ err = sprintf('%s, and %s.',err,formats{end});
+ else
+ err = sprintf('%s.',err);
+ end
+ return;
+end
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% CHECKDEFAULTS :: Check the specified default values are compatible
+%%% with Formats and if one not given fill in an initial value
+function [DefAns,TileMode,err] = checkdefaults(DefAns,Formats,FieldNames)
+% AnsStr:
+
+% set the TileMode
+TileMode = [1 1]; % default: no tiling
+dims = size(Formats); % grid dimension
+if any(dims==1) % To tile, formats must be given as a vector
+ tiledim = (dims(2)==1) + 1; % if 1, tile rows; if 2, tile columns
+ ansdims = size(DefAns);
+ tf = ansdims>1;
+ if isstruct(DefAns) && any(tf) % if vector, tile
+ TileMode(tiledim) = numel(DefAns);
+ elseif iscell(DefAns) && all(tf) % if matrix, tile
+ TileMode(tiledim) = ansdims(2);
+ end
+end
+
+if all(TileMode==1) % no tiling
+ DefAns = DefAns(:);
+end
+
+% reshape to a "row" vector & trim Formats to only include relevant entries (non-'none' types)
+Formats = Formats'; % go through row first
+Formats = Formats(~strcmp('none',{Formats.type}));
+len = numel(Formats); % if tiled, expects len*prod(TileMode) DefAns
+
+if isempty(DefAns) % if DefAns not given
+ DefAns = cell(len,1); % will set DefAns to default values
+elseif isstruct(DefAns)
+ if isempty(FieldNames) % FieldNames must be given via PROMPT
+ err = {'inputsdlg:InvalidInput','DEFAULTANSWER is given in as a struct but its field names are not specified in PROMPT (the 2nd column).'};
+ return;
+ end
+
+ % Convert Struct to cell according to FieldNames
+ Inotempty = find(~cellfun(@isempty,FieldNames)); % ignore prompt w/o FieldName
+ [~,I] = ismember(fieldnames(DefAns),FieldNames(Inotempty));
+ if any(I==0)
+ err = {'inputsdlg:InvalidFormatsField','DEFAULTANSWER contains invalid field name(s).'};
+ return
+ end
+ DefStr = DefAns; % save the original input
+ DefAns = cell(len,prod(TileMode)); % len==numel(FieldNames) is guaranteed
+ DefAns(Inotempty(I),:) = struct2cell(DefStr(:));
+
+elseif ~iscell(DefAns)
+ err = {'inputsdlg:InvalidInput','Default answer must be given in a cell array or a structure.'};
+ return;
+elseif length(DefAns)~=len
+ err = {'inputsdlg:InvalidInput','Default answer cell dimension disagrees with the number of prompt'};
+ return;
+end
+
+% go through each default values
+for k = 1:len
+
+ fmt = Formats(k);
+
+ % if any of the tiled element is given empty & its answer is required
+ Iempty = cellfun(@isempty,DefAns(k,:));
+ if any(Iempty) && strcmp(Formats(k).required,'on')
+
+ switch fmt.type
+ case 'check' % off
+ if strcmp(fmt.format,'logical')
+ val = false;
+ else
+ val = fmt.limits(1);
+ end
+ case 'edit'
+ switch fmt.format
+ case {'float','integer'}
+ liminf = isinf(fmt.limits);
+ if all(liminf) % both limits inf
+ val = 0;
+ elseif any(liminf) % 1 limit inf
+ val = fmt.limits(~liminf);
+ else % neither is inf
+ val = round(mean(fmt.limits));
+ end
+ case 'vector'
+ val = zeros(1,fmt.limits(1));
+ otherwise %{'text','date','file','dir'}
+ val = '';
+ end
+ case 'list' % first item
+ val = 1;
+ if strcmp(fmt.format,'text')
+ val = fmt.items{find(~cellfun(@isempty,fmt.items),1)};
+ end
+ case 'range' % middle value
+ val = mean(fmt.limits);
+ case 'color'
+ val = [1 1 1];
+ case 'table'
+ val = {};
+ otherwise
+ val = [];
+ end
+
+ % set the default value to all empty controls on all tiles
+ [DefAns{k,Iempty}] = deal(val);
+ end
+
+ % for all entries that are not empty check the validity of given values
+ Ifilled = ~Iempty;
+ if ~strcmp(fmt.type,'table') && any(Ifilled)
+
+ vals = DefAns(k,Ifilled); % given default values
+
+ switch fmt.format
+ case 'text'
+ % must be char or cellsctr
+ Jischar = cellfun(@ischar,vals);
+ Jiscellstr = cellfun(@iscellstr,vals);
+ if ~all(Jischar|Jiscellstr)
+ err = {'inputsdlg:InvalidInput','Default text data format must be char.'};
+ return;
+ end
+
+ % for the list type, value must be one of the allowed item
+ if strcmp(fmt.type,'list')
+ isUiControl = ismember(fmt.style,{'listbox', 'popupmenu'});
+ if any(Jischar)
+ try
+ vals(:) = cellfun(@(v)validatestring(v,fmt.items),vals,'UniformOutput',false);
+ catch ME
+ err = {'inputsdlg:InvalidInput',ME.message};
+ return
+ end
+ if isUiControl
+ [~,idx] = ismember(vals,fmt.items);
+ % convert to integer format
+ vals(Jischar) = num2cell(idx);
+ end
+ end
+ if any(Jiscellstr)
+ Jiscellstr = find(Jiscellstr);
+ for j = Jiscellstr(:).'
+ [tf,idx] = ismember(vals{j},fmt.items);
+ if ~all(tf)
+ err = {'inputsdlg:InvalidInput','Default list item is not valid.'};
+ return;
+ end
+ % convert to integer format
+ if isUiControl
+ vals{j} = idx;
+ end
+ end
+ end
+
+ DefAns(k,Ifilled) = vals;
+ end
+
+ case 'date' % given as a date vector
+ % must be a date string or date number or date vector
+ if ~all(cellfun(@(v)ischar(v)||isnumeric(v),vals))
+ err = {'inputsdlg:InvalidInput','Default date date must be a valid datenum, datestr, or datevec.'};
+ return;
+ end
+ % store the date as a string
+ try
+ DefAns(k,Ifilled) = cellfun(@(v)datestr(v, fmt.limits),vals,'UniformOutput',false);
+ catch % not a valid date input given
+ err = {'inputsdlg:InvalidInput','Default date date must be a valid datenum, datestr, or datevec.'};
+ return;
+ end
+ case 'float'
+ if strcmp(fmt.type,'color') % must be a valid RGB tuple
+ attr = {'numel',3,'nonnegative','<=',1};
+ varname = 'Default color data';
+ else % for all other types, value must be scalar
+ if iscell(fmt.limits)
+ attr = [{'scalar'} fmt.limits];
+ else
+ attr = {'scalar', '>=',fmt.limits(1),'<=',fmt.limits(2)};
+ end
+ varname = 'Default float data';
+ end
+
+ try
+ cellfun(@(v)validateattributes(v,{'numeric'},attr,mfilename,varname),vals);
+ catch ME
+ err = {'inputsdlg:InvalidInput',ME.message};
+ return;
+ end
+
+ case 'integer' % can be multi-select if type=list
+ switch fmt.type
+ case 'list'
+ isUiBtnGrp = ismember(fmt.style,{'togglebutton', 'radiobutton'});
+ msel = strcmp(fmt.style,'listbox') && diff(fmt.limits)>1;
+
+ % must be a valid index to items and
+ % if multiple-selection is not enabled, must be scalar
+ attr = {'integer','positive','<=',numel(fmt.items)};
+ if ~msel
+ attr{end+1} = 'scalar'; %#ok
+ end
+
+ try
+ cellfun(@(v)validateattributes(v,{'numeric'},attr,mfilename,...
+ 'Default list index data'),vals);
+ catch ME
+ err = {'inputsdlg:InvalidInput',ME.message};
+ return;
+ end
+
+ if msel % if multiple-selection enabled, make sure values are unique
+ DefAns(k,Ifilled) = cellfun(@unique,vals,'UniformOutput',false);
+ elseif isUiBtnGrp
+ DefAns(k,Ifilled) = vals;%fmt.items(vals);
+ end
+
+ case 'color' % must be a RGB tuple
+ try
+ cellfun(@(v)validateattributes(v,{'numeric'},...
+ {'numel',3,'integer','nonnegative','<=',255},mfilename,...
+ 'Default color data'),vals);
+ catch ME
+ err = {'inputsdlg:InvalidInput',ME.message};
+ return;
+ end
+
+ % convert to float representation
+ DefAns(k,Ifilled) = cellfun(@(v)double(v)/255,vals,'UniformOutput',false);
+ case 'check' % must be a scalar and one of limits
+ if ~all(cellfun(@(v)isscalar(v) && any(v==fmt.limits)))
+ err = {'inputsdlg:InvalidInput','Default integer check data must be a scalar value matching Format.Limits'};
+ return;
+ end
+ otherwise %must be a scalar
+ % limits specifies the value range
+ if iscell(fmt.limits)
+ attr = [{'scalar','integer'} fmt.limits];
+ else
+ attr = {'scalar','integer','>=' fmt.limits(1),'<=',fmt.limits(2)};
+ end
+ try
+ cellfun(@(v)validateattributes(v,{'numeric'},attr,mfilename,'Defaultinteger data'),vals);
+ catch ME
+ err = {'inputsdlg:InvalidInput',ME.message};
+ return;
+ end
+ end
+
+ case 'logical'
+
+ if ~all(cellfun(@(v)islogical(v)&&isscalar(v),vals))
+ err = {'inputsdlg:InvalidInput','Default logical data must be of logical or numeric scalar.'};
+ return;
+ end
+
+ % convert to integer format
+ DefAns(k,Ifilled) = cellfun(@(v)fmt.limits(v+1),vals,'UniformOutput',false);
+
+ case 'file'
+ % must be char or cellstring
+ if ~all(cellfun(@(v)ischar(v)||iscellstr(v),vals))
+ err = {'inputsdlg:InvalidInput','Default file data must be given as char or cellstr.'};
+ return;
+ end
+
+ % make everything cellstr
+ vals = cellfun(@cellstr,vals,'UniformOutput',false);
+
+ dlim = diff(fmt.limits);
+
+ if dlim<=1 && ~all(cellfun(@isscalar,vals)) % single-file control
+ err = {'inputsdlg:InvalidInput','Multiple default files are given for single-file control.'};
+ return;
+ end
+
+ if dlim>=0 % for uigetfile, either directory name or existing file name
+ if ~any(cellfun(@(v)all(cellfun(@(file)exist(file,'file'),v)),vals))
+ err = {'inputsdlg:InvalidInput','Default file for uigetfile must exist on the computer.'};
+ return;
+ end
+
+ % resolve full file name, if only name is given for a file
+ % on matlab path
+ changed = false;
+ for n = 1:prod(TileMode)
+ files = cellfun(@which,vals{n},'UniformOutput',false);
+ I = ~cellfun(@isempty,files);
+ changed = changed || any(I);
+ vals{n}(I) = files(I);
+
+ % multi-select files must be on a same directory
+ dirs = cellfun(@fileparts,vals{n},'UniformOutput',false);
+ if ispc
+ dirs = lower(dirs);
+ end
+ if numel(unique(dirs))>1
+ err = {'inputsdlg:InvalidInput','Default files for multi-select control must be from a same directory.'};
+ return;
+ end
+ end
+
+ if changed
+ DefAns(k,Ifilled) = vals;
+ end
+ end
+ case 'dir'
+ if ~all(cellfun(@(v)ischar(v)&&isrow(v)&&isdir(v),vals)) % directory must exist
+ err = {'inputsdlg:InvalidInput','Default dir must be a valid path.'};
+ return;
+ end
+ case 'vector'
+ iscol = true;
+ if iscell(fmt.limits)
+ if any(strcmpi(fmt.limits,'row'))
+ iscol = false;
+ attr = fmt.limits;
+ else
+ attr = [{'column'},fmt.limits];
+ vals{1} = vals{1}(:);
+ end
+ try
+ cellfun(@(v)validateattributes(v,{'numeric'},attr,mfilename,'Default vector data'),vals);
+ catch ME
+ err = {'inputsdlg:InvalidInput',ME.message};
+ return;
+ end
+ else
+ nel = numel(vals{1});
+ if any(cellfun(@(v)~isnumeric(v) || nelfmt.limits(2),vals))
+ err = {'inputsdlg:InvalidInput','Default vector data must be numeric and the number of elements must be within the limit.'};
+ return;
+ end
+ end
+ if iscol
+ idx = cellfun(@(v)~iscolumn(v),vals);
+ DefAns(k,Ifilled) = cellfun(@(v)v(:),vals(idx),'UniformOutput',false);
+ end
+ end
+ end
+end
+
+DefAns = DefAns(:);
+err = {}; % all good
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+function [Options,FormatFields,err] = checkoptions(UserOptions)
+
+err = {'inputsdlg:InvalidInput',''};
+
+% default options
+Fields = {
+ 'Resize' 'off'
+ 'WindowStyle' 'normal'
+ 'Interpreter' 'tex'
+ 'DialogSizeMinimum' [inf inf] % automatically set
+ 'CancelGiven' false
+ 'CancelButton' 'on'
+ 'ApplyGiven' false
+ 'ApplyButton' 'off'
+ 'ButtonNames' {{'OK','Cancel','Apply'}}
+ 'Sep' 10
+ 'AlignControls' 'off'
+ 'FontSize' get(0,'DefaultUicontrolFontSize')
+ 'CreateFcn' {{}}
+ 'DeleteFcn' {{}}
+ }.';
+
+% default formats, given type & format
+FormatFields = [
+ {'type' 'format' 'style' 'items' 'limits' 'size' 'enable' 'required' 'callback' 'labelloc' 'unitsloc' 'margin'}
+ {'edit' 'text' 'edit' {} [0 1] [0 0] 'on' 'off' struct([]) 'leftmiddle' 'righttop' [3 3]} % default if Formats or Formats.type not given
+ {'edit' 'integer' 'edit' {} [-inf inf] [0 0] 'on' 'off' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'edit' 'float' 'edit' {} [-inf inf] [0 0] 'on' 'off' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'edit' 'vector' 'edit' {} [0 inf] [0 0] 'on' 'off' struct([]) 'lefttop' 'righttop' [3 3]}
+ {'edit' 'date' 'edit' {} 2 [0 0] 'on' 'off' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'edit' 'file' 'edit' {'*.*' 'All Files'} [0 1] [0 0] 'on' 'off' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'edit' 'dir' 'edit' {} [0 1] [0 0] 'on' 'off' struct([]) 'leftmiddle' 'righttop' [3 3]} % default if Formats or Formats.type not given
+ {'check' 'logical' 'checkbox' {} [0 1] [0 0] 'on' 'on' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'check' 'integer' 'checkbox' {} [0 1] [0 0] 'on' 'on' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'check' 'text' 'checkbox' {} {'off','on'} [0 0] 'on' 'on' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'list' 'integer' 'popupmenu' {} [0 1] [0 0] 'on' 'on' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'list' 'text' 'popupmenu' {} [0 1] [0 0] 'on' 'on' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'range' 'float' 'slider' {} [0 1] [0 0] 'on' 'on' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'range' 'integer' 'slider' {} [0 255] [0 0] 'on' 'on' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'color' 'float' 'pushbutton' {} [0 1] [65 0] 'on' 'on' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'color' 'integer' 'pushbutton' {} [0 255] [65 0] 'on' 'on' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'button' '' 'pushbutton' {} [0 1] [0 0] 'on' 'off' struct([]) 'leftmiddle' 'righttop' [3 3]}
+ {'text' '' 'text' {} [0 1] [0 0] 'on' 'off' struct([]) 'lefttop' 'righttop' [3 3]}
+ {'none' '' '' {} [0 0] [0 0] 'on' 'off' struct([]) 'lefttop' 'righttop' [3 3]} % default if Formats.type empty
+ {'table' {} 'table' {} {'auto'} [0 0] 'on' 'off' struct([]) 'lefttop' 'righttop' [3 3]}];
+
+Options = struct(Fields{:});
+
+if isempty(UserOptions) % no option specified, use default
+ err = {};
+ return;
+elseif ischar(UserOptions) && any(strcmpi(UserOptions,{'on','off'}))
+ warning off MATLAB:warn_r14_stucture_assignment
+ UserOptions.Resize = UserOptions;
+ warning on MATLAB:warn_r14_stucture_assignment
+elseif ~isstruct(UserOptions)
+ err{2} = 'Options must be ''on'', ''off'', or a struct.';
+ return;
+end
+
+if numel(UserOptions)~=1
+ err{2} = 'Options struct must be a scalar.';
+ return;
+end
+
+% check if User Resize Option is given as on/off string
+
+% to-do: Separate overall options to format options
+% optfnames = fieldnames(UserOptions);
+% optnames = regexpi(optfnames,'default(\S+)','tokens');
+% Iopt = ~cellfun(@isempty,optnames);
+% optnames = lower(cellfun(@(c)c{1},[optnames{Iopt}],'UniformOutput',false));
+% fmtnames = lower(FormatFields(1,:));
+% nfmtfields = numel(fmtnames); % sans the first row
+
+% remove all the format options
+% Options = rmfield(Options,optfnames(Iopt)));
+
+% start with 'type'-'format' combo for the default control type
+% [tf,I] = ismember(optnames,fnames);
+
+
+% check UserOptions struct & update Options fields
+for fname_cstr = fieldnames(UserOptions)' % for each user option field
+
+ fname = char(fname_cstr); % use plain char string (not cellstr)
+ val = UserOptions.(fname);
+
+ % make sure string value is given as a cellstr
+ if ischar(val), val = cellstr(val); end
+
+ % if field not filled, use default value
+ if isempty(UserOptions.(fname)), continue; end
+
+ switch lower(fname)
+ case 'resize'
+ if numel(val)~=1 || ~any(strcmpi(val,{'on','off'}))
+ err{2} = 'Resize option must be ''on'' or ''off''.';
+ return;
+ end
+ Options.Resize = char(val);
+ case 'windowstyle'
+ if numel(val)~=1 || ~any(strcmpi(val,{'normal','modal','docked'}))
+ err{2} = 'WindowStyle option must be ''normal'' or ''modal''.';
+ return;
+ end
+ Options.WindowStyle = char(val);
+ case 'interpreter'
+ if numel(val)~=1 || ~any(strcmpi(val,{'latex','tex','none'}))
+ err{2} = 'Interpreter option must be ''latex'', ''tex'', or ''none''.';
+ return;
+ end
+ Options.Interpreter = char(val);
+ case 'cancelbutton'
+ if numel(val)~=1 || ~any(strcmpi(val,{'on','off'}))
+ err{2} = 'CancelButton option must be ''on'' or ''off''.';
+ return;
+ end
+ Options.CancelGiven = true;
+ Options.CancelButton = char(val);
+ case 'applybutton'
+ if numel(val)~=1 || ~any(strcmpi(val,{'on','off'}))
+ err{2} = 'ApplyButton option must be ''on'' or ''off''.';
+ return;
+ end
+ Options.ApplyGiven = true;
+ Options.ApplyButton = char(val);
+ case 'buttonnames'
+ if ~iscellstr(val)
+ err{2} = 'ButtonNames option must be of cellstr or char type.';
+ return;
+ end
+
+ % if not all 3 button names are given, use default for unspecified
+ N = numel(val);
+ if (N>3)
+ err{2} = 'ButtonNames option takes up to 3 button names.';
+ return;
+ end
+ Options.ButtonNames(1:N) = val;
+ case 'sep'
+ if numel(val)~=1 || ~isnumeric(val) || val<0
+ err{2} = 'Sep option must be non-negative scalar value.';
+ return;
+ end
+ Options.Sep = val;
+ case 'aligncontrols'
+ if numel(val)~=1 || ~any(strcmpi(val,{'on','off'}))
+ err{2} = 'AlignControls option must be ''on'' or ''off''.';
+ return;
+ end
+ Options.AlignControls = char(val);
+ case 'unitsmargin'
+ error('UnitMargin option has been deplicated. Use Formats.margin to set individual control''s label margins.');
+ case 'fontsize'
+ if ~(isscalar(val) && isnumeric(val) && val>0)
+ err{2} = 'FontSize option must be non-negative scalar value.';
+ return;
+ end
+ Options.FontSize = val;
+ case 'dialogsizeminimum'
+ if ~(isnumeric(val) && numel(val)==2 && ~any(isnan(val)))
+ err{2} = 'DialogSizeMinimum option must be 2-element vector.';
+ end
+ % no minimum if not positive
+ idx = val<=0;
+ Options.DialogSizeMinimum(idx) = inf;
+ case 'createfcn'
+ if ~(isempty(val) || isa(val,'function_handle'))
+ err{2} = 'CreateFcn option must be a function handle.';
+ end
+ Options.CreateFcn = val;
+ case 'deletefcn'
+ if ~(isempty(val) || isa(val,'function_handle'))
+ err{2} = 'DeleteFcn option must be a function handle.';
+ end
+ Options.DeleteFcn = val;
+ otherwise
+ warning('inputsdlg:InvalidOption','%s is not a valid option name.',fname);
+ end
+end
+
+err = {}; % all cleared
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% BUILDGUI :: Builds the dialog box and returns handles
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+function [Formats,handles,sinfo] = buildgui(Title,Prompt,Units,TooltipStrings,FieldNames,Formats,TileMode,Options)
+% 1. Create handle graphic objects for all controls (embedded in uipanels)
+% 2. Generate sinfo to assist object positioning in doResize
+
+sep = Options.Sep;
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Tile the controls
+coltile = TileMode(2)>1;
+if any(TileMode>1)
+
+ Formats = repmat(Formats,TileMode);
+
+ num = numel(Prompt)*prod(TileMode);
+ Prompt = reshape(repmat(Prompt,TileMode),num,1);
+ FieldNames = reshape(repmat(FieldNames,TileMode),num,1);
+ Units = reshape(repmat(Units,TileMode),num,1);
+ TooltipStrings = reshape(repmat(TooltipStrings,TileMode),num,1);
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% determine how to utilize 'none' space
+% Place all the elements at (0,0)
+dim = size(Formats); % display grid dimension
+free = strcmp('none',{Formats.type}); % location of empty block maybe to be occupied by neighbor entry
+num = sum(~free); % number of controls
+map = zeros(dim); % determine which control occupies which block(s)
+order = zeros(1,num); % uicontrol placement order (for tab order)
+wcell = zeros(dim); % -> widths of cells
+hcell = zeros(dim); % -> heights of cells
+autoheight = false(dim(1),1);
+autowidth = false(1,dim(2));
+
+n = 1;
+for f = 1:prod(dim)
+ if coltile % traverse column-first
+ [i,j] = ind2sub(dim,f);
+ else % traverse row-first
+ [j,i] = ind2sub(dim([2 1]),f);
+ end
+ m = sub2ind(dim,i,j);
+
+ if free(m)
+ mode = diff(Formats(m).limits);
+ [i,j] = ind2sub(dim,m);
+
+ if mode>0 && j>1, map(m) = map(sub2ind(dim,i,j-1)); % copy from left
+ elseif mode<0 && i>1, map(m) = map(sub2ind(dim,i-1,j)); % copy from above
+ end % other wise, 0 (nothing occupying)
+ else
+ map(m) = n;
+ order(n) = m;
+ n = n + 1;
+ end
+end
+
+% Check none-type cells, possibly acting as a placeholder
+[I,J] = find(reshape(free,dim));
+for n = 1:numel(I)
+ sz = Formats(I(n),J(n)).size;
+ if sz(1)>0 % placeholding for fixed row height
+ hcell(I(n),J(n)) = sz(1);
+ elseif sz(1)<0 % placeholding with minimum row height
+ hcell(I(n),J(n)) = -sz(1);
+ autoheight(I(n)) = true;
+ end
+ if sz(2)>0 % placeholding for fixed column width
+ wcell(I(n),J(n)) = sz(2);
+ elseif sz(2)<0 % placeholding with minimum row height
+ wcell(I(n),J(n)) = -sz(2);
+ autowidth(I(n)) = true;
+ end
+end
+
+% remove none's from Formats and order the rest in Prompt order
+Formats = Formats(order).'; % removes all none-types
+
+FigColor=get(0,'DefaultUicontrolBackgroundcolor');
+
+fig = dialog( ...
+ 'Visible' ,'off' , ...
+ 'Name' ,Title , ...
+ 'Pointer' ,'arrow' , ...
+ 'Units' ,'pixels' , ...
+ 'UserData' ,'Cancel' , ...
+ 'Tag' ,'Inputsdlg' , ...
+ 'HandleVisibility' ,'callback' , ...
+ 'Color' ,FigColor , ...
+ 'WindowStyle' ,Options.WindowStyle, ...
+ 'DoubleBuffer' ,'on' , ...
+ 'Resize' ,Options.Resize ...
+ );
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Create controls
+%%%%%%%%%%%%%%%%%%%%%
+CommonInfo = {'Units' 'pixels'
+ 'FontSize' Options.FontSize
+ 'FontWeight' get(0,'DefaultUicontrolFontWeight');
+ 'HandleVisibility' 'callback'}';
+
+props.edit = [CommonInfo {...
+ 'Style' 'edit';
+ 'HorizontalAlignment' 'left';
+ 'BackgroundColor' 'white'}'];
+
+props.checkbox = [CommonInfo {...
+ 'Style' 'checkbox';
+ 'HorizontalAlignment' 'left';
+ 'BackgroundColor' FigColor}'];
+
+props.popupmenu = [CommonInfo {...
+ 'Style' 'popupmenu';
+ 'HorizontalAlignment' 'left';
+ 'BackgroundColor' 'white'}'];
+
+props.listbox = [CommonInfo {...
+ 'Style' 'listbox';
+ 'HorizontalAlignment' 'left';
+ 'BackgroundColor' 'white'}'];
+
+props.slider = [CommonInfo {...
+ 'Style' 'slider';
+ }'];
+
+props.uibuttongroup = [CommonInfo {...
+ 'BackgroundColor' FigColor
+ }'];
+
+props.radiobutton = props.checkbox;
+props.radiobutton{2,strcmp(props.checkbox(1,:),'Style')} = 'radiobutton';
+
+props.pushbutton = [CommonInfo {...
+ 'Style' 'pushbutton';
+ 'HorizontalAlignment' 'center'}'];
+
+props.togglebutton = props.pushbutton;
+props.togglebutton{2,strcmp(props.pushbutton(1,:),'Style')} = 'togglebutton';
+
+props.table = CommonInfo;
+
+% Add VerticalAlignment here as it is not applicable to the above.
+props.text = [CommonInfo {...
+ 'Style' 'text'
+ 'Units' 'normalized'
+ 'Position' [0 0 1 1]
+ 'Visible' 'off'}'];
+
+props.label = [CommonInfo {...
+ 'BackgroundColor' FigColor;
+ 'HorizontalAlignment' 'left';
+ 'VerticalAlignment' 'bottom';
+ 'Color' get(0,'FactoryUIControlForegroundColor');
+ 'Interpreter' Options.Interpreter}'];
+
+% For each control, place a uicontrol and an axes (axes is to enable LaTeX)
+figsz = get(fig,'Position');
+props.uipanel = [CommonInfo {...
+ 'BackgroundColor' FigColor
+ 'BorderType','none'
+ }.'];
+props.axes = [CommonInfo {...
+ 'Units' 'normalized'
+ 'Position' [0 0 1 1]
+ 'XLim' [0 figsz(3)]
+ 'YLim' [0 figsz(4)]
+ 'Visible' 'off'}.'];
+
+isbtngrp = false(num,1);
+istext = reshape(arrayfun(@(s)strcmp(s.type,'text'),Formats),num,1);
+autosize = zeros(num,2); % 0-fixed, 1-autosize, 2-resize with window
+rowspan = zeros(num,1); % # of rows control occupies
+colspan = zeros(num,1); % # of columns control occupies
+hPanels = zeros(num,2); % [uipanel|axes]
+hCtrls = zeros(num,3); % [Prompt|Edit|Unit]
+for m = 1:num % for each control
+
+ % get current control's Format spec
+ fmt = Formats(m);
+
+ % determine the span of the control
+ [i,j] = find(map==m);
+ rowspan(m) = numel(unique(i));
+ colspan(m) = numel(unique(j));
+
+ % set autosize (if fixed, set only once)
+ % autosize(m,:) = (fmt.size<0) + (fmt.size<=0);
+ autosize(m,:) = (fmt.size<=0);
+
+ % if autosize, set to default size, if fixed
+ if autosize(m,1) % width
+ if fmt.size(1)==0
+ fmt.size(1) = 1; % temp size
+ else
+ fmt.size(1) = -fmt.size(1);
+ end
+ end
+ if autosize(m,2) % height
+ if fmt.size(2)==0
+ fmt.size(2) = 1; % temp size
+ else
+ fmt.size(2) = -fmt.size(2);
+ end
+ end
+
+ % for uicontrols
+ hPanels(m,1) = uipanel('Parent',fig,'Position',[0 0 fmt.size],props.uipanel{:});
+
+ % for text controls & labels
+ hPanels(m,2) = axes(props.axes{:},'Parent',hPanels(m,1));
+
+ % always add labels even if control is not using them
+ hCtrls(m,2) = text('Parent',hPanels(m,2),props.label{:},'String',Prompt{m});
+ hCtrls(m,3) = text('Parent',hPanels(m,2),props.label{:},'String',Units{m});
+
+ idx = strcmp(fmt.style,{'radiobutton','togglebutton'});
+ isbtngrp(m) = any(idx);
+ if isbtngrp(m)
+ % create the UI Button Group object
+ h = uibuttongroup('Parent',hPanels(m,1),props.uibuttongroup{:},...
+ 'Position',[0 0 fmt.size],'Tag',FieldNames{m});%,'Title',Prompt{m}
+ hCtrls(m,1) = h;
+
+ % Create button objects and record their extents
+ dim_btns = size(fmt.items);
+ kvalid = find(~cellfun(@isempty,fmt.items));
+ Nvalid = numel(kvalid);
+ hButtons = zeros(Nvalid,1);
+ btn_w = zeros(dim_btns);
+ btn_h = zeros(dim_btns);
+ for n = 1:numel(kvalid)
+ [i,j] = ind2sub(dim_btns,kvalid(n));
+ hButtons(n) = uicontrol('Parent',h,'Style',fmt.style,props.(fmt.style){:},...
+ 'String',fmt.items{i,j},'Min',0,'Max',1,'UserData',kvalid(n),...
+ 'TooltipString',TooltipStrings{m});
+ pos = get(hButtons(n),'Extent');
+ btn_w(i,j) = pos(3);
+ btn_h(i,j) = pos(4);
+ end
+
+ % set buttons sizes and extra margins to account for the button width
+ if idx(1) % radiobutton
+ % each column to have the same width
+ margin = [20 0];
+ btn_w = max(btn_w,[],1) + margin(1); % button widths with extra pixels for non-text part of control
+ else % togglebutton
+ % all buttons to have the same width
+ margin = [12 2];
+ btn_w = repmat(max(btn_w(:)) + margin(1),1,dim_btns(2)); % button widths with extra pixels for non-text part of control
+ end
+ btn_h = max(btn_h,[],2) + margin(2); % button heights with extra pixels for non-text part of conrol
+
+ % Button positions
+ btn_sep = sep*3/4;
+ x0 = cumsum([0 btn_w]+btn_sep)-btn_sep*3/8;
+ y0 = flipud(cumsum([0;btn_h]+btn_sep)-btn_sep*3/8);
+
+ % set positions of buttons
+ kvalid = find(hButtons~=0);
+ for n = 1:Nvalid
+ [i,j] = ind2sub(dim_btns,kvalid(n)); % i-col, j-row
+ pos = [x0(j) y0(i+1) btn_w(j) btn_h(i)];
+ set(hButtons(n),'Position',pos);
+ end
+
+ % set the size
+ set(h,'Position',[0 0 x0(end) y0(1)],'UserData',hButtons);
+ autosize(m,:) = 0; % no autosize
+
+ elseif strcmp(fmt.style,'table') % uitable
+
+ hCtrls(m,1) = uitable('Parent',hPanels(m,1), props.(fmt.style){:}, ...
+ 'Position',[0 0 fmt.size],'ColumnName',fmt.items,...
+ 'ColumnFormat',fmt.format,'ColumnWidth',fmt.limits,...
+ 'ColumnEditable',true,...
+ 'Tag',FieldNames{m});
+
+ else % uicontrols
+
+ % create a uipanel, embed a uicontrol, and 2 text labels
+ hc = uicontrol('Parent',hPanels(m,1), 'Style',fmt.style, props.(fmt.style){:},...
+ 'Position',[0 0 fmt.size],'Tag',FieldNames{m},'TooltipString',TooltipStrings{m});
+ hCtrls(m,1) = hc;
+
+ % set min and max if not a numeric edit box
+ if ~(any(strcmp(fmt.style,'edit')) && any(strcmp(fmt.format,{'float','integer','date','dir','vector'}))) ...
+ && ~(strcmp(fmt.type,'check')&&strcmp(fmt.format,'text'))
+ lim = fmt.limits;
+ if any(isinf(lim)), lim = [0 2]; end % edit:vector
+ set(hc,'Min',lim(1),'Max',lim(2));
+ end
+
+ % style-dependent configuration
+ switch fmt.style
+ case 'text' % static text (only a label)
+
+ % display the label on the upper left hand corner of the display grid cell
+ set(hCtrls(m,2),'Units','normalized','Position',[0 1],'VerticalAlignment','top');
+
+ % create invisible dummy control
+ set(hc,'FontName',get(hCtrls(m,2),'FontName'),'String',Prompt{m});
+
+ % if not autowidth, go ahead and map-out
+ if ~autosize(m,1)
+ set(hc,'Position',[0 0 fmt.size]);
+ msg = textwrap(hc,Prompt(m));
+ str = sprintf('%s\n',msg{:});
+ set(hc,'String',str(1:end-1));
+ autosize(m,2) = 0; % only auto-height if auto-width
+ end
+
+ % Use Axes Text object (in order to render LaTeX text)
+ set(hCtrls(m,3),'String','','Visible','off');
+
+ case 'edit' % edit type
+
+ % check if multi-line control
+ if iscell(fmt.limits)
+ dlim = 0;
+ else
+ dlim = round(diff(fmt.limits));
+ end
+
+ % set multi-line edit box
+ multiline = (any(strcmp(fmt.format,{'text','file'})) && dlim>1);
+ if multiline
+ nrows = dlim-1;
+ else
+ multiline = strcmp(fmt.format,'vector');
+ if multiline
+ if iscell(fmt.limits)
+ multiline = ~any(strcmpi(fmt.limits,'row')); % if row-vector, a single-line editbox suffices
+ if multiline
+ nrows = find(strcmpi(fmt.limits,'numel'),1);
+ if isempty(nrows)
+ nrows = 5;
+ else
+ nrows = min(5,fmt.limits{nrows+1});
+ end
+ end
+ else
+ nrows = max(fmt.limits(1),min(fmt.limits(2),5));
+ end
+ end
+ end
+ if multiline
+ set(hc,'Min',0,'Max',2);
+ end
+
+ % change alignment for numeric formats (default: center)
+ if any(strcmp(fmt.format,{'float','integer'}))
+ set(hc,'HorizontalAlignment','center');
+ elseif strcmp(fmt.format,'vector')
+ set(hc,'HorizontalAlignment','left');
+ end
+
+ % set auto-height (no resize allowed if single-line)
+ if autosize(m,2)>0 % auto-height adjustment
+ % set
+ if multiline % set to have dlim lines
+ set(hc,'String',repmat(sprintf(' \n'),1,nrows-1));
+ else % single-line
+ set(hc,'String',' ');
+ autosize(m,2) = 0; % no need to adjust dynamically
+ end
+ ext = get(hc,'Extent');
+ set(hc,'String','');
+ fmt.size(2) = ext(4); % set to the font height
+ set(hc,'Position',[0 0 fmt.size]);
+ end
+
+ case 'checkbox' % no labels
+
+ % Show the prompt label with the control
+ set(hc,'String',Prompt{m});
+ set(hCtrls(m,2),'String','','Visible','off');
+
+ % Set the control size (fixed, no resize)
+ pos = get(hc,'Extent'); % width reflects only label width
+ pos(3) = pos(3) + 20; % pad extra for the checkbox itself
+ set(hc,'Position',pos);
+ autosize(m,:) = 0; % no resizing
+
+ case 'popupmenu'
+
+ % get the width of the widest entry
+ if autosize(m,1) % auto-width
+ w = 0;
+ for n = 1:numel(fmt.items)
+ set(hc,'String',fmt.items{n});
+ ext = get(hc,'Extent');
+ w = max(w,ext(3));
+ end
+ fmt.size(1) = w + 20; % additional width for the pulldown
+ end
+
+ if autosize(m,2) % auto-height
+ % only list 1 item and get the extent
+ set(hc,'String',fmt.items{1});
+ ext = get(hc,'Extent');
+ fmt.size(2) = ext(4);
+ end
+
+ % re-set position
+ if any(autosize(m,:))
+ set(hc,'Position',[0 0 fmt.size]);
+ autosize(m,:) = 0; % no resizing
+ end
+
+ % Set menu & choose the first entry
+ set(hc, 'String',fmt.items);
+
+ case 'listbox'
+
+ % Set menu & choose the first entry
+ set(hc,'String',fmt.items);
+
+ if any(autosize(m,:))
+ % determine the optimal size
+ ext = get(hc,'Extent');
+ if autosize(m,1) % auto-width
+ fmt.size(1) = ext(3) + 20;
+ if autosize(m,2)~=1 && fmt.size(2) set to the tallest
+ % Restrict the height if a maximum number of lines was specified
+ if fmt.limits(1) > 0 && fmt.limits(1) < numel(fmt.items)
+ set(hc,'String',fmt.items(1:fmt.limits(1)));
+ ext = get(hc,'Extent');
+ set(hc,'String',fmt.items);
+ end
+ fmt.size(2) = ext(4);
+ if fmt.limits(1)>0
+ autosize(m,2) = 0;
+ end
+ end
+
+ % re-set position
+ set(hc,'Position',[0 0 fmt.size]);
+ end
+
+ case 'slider'
+
+ if any(autosize(m,:))
+ if autosize(m,1) && ~autosize(m,2) % vertical slider, auto-width -> fixed width
+ fmt.size(1) = 16;
+ autosize(m,1) = 0;
+ elseif autosize(m,2) % auto-height -> fixed height
+ fmt.size(2) = 16;
+ autosize(m,2) = 0;
+ end
+ set(hc,'Position',[0 0 fmt.size]);
+ end
+
+ % set slider step if items field is filled
+ if ~isempty(fmt.items)
+ set(hc,'SliderStep',fmt.items);
+ end
+
+ case 'pushbutton' % button & color types
+
+ if strcmp(fmt.type,'color') % color type
+ set(hc,'String',' ') % space to set auto-height
+ else % button type
+ % Show the prompt label with the control
+ set(hc,'String',Prompt{m});
+ set(hCtrls(m,2),'String','','Visible','off');
+ end
+
+ if autosize(m,2) % auto-height -> fix
+ ext = get(hc,'Extent');
+ fmt.size(2) = ext(4) + 6;
+ set(hc,'Position',[0 0 fmt.size]);
+ autosize(m,2) = 0;
+ end
+ end
+ end
+end % for m = 1:num
+
+% layout each control and labels within the panel (and axes)
+labelbase = zeros(num,2,2); % lower-left corner coordinates
+labelpos = zeros(num,2,2);
+labelsiz = zeros(num,2,2);
+labelalign = ones(num,4);
+ctrlpos = zeros(num,4);
+set(hCtrls(istext,2),'Units','pixels');
+alignprops = cell(2,2); % {'HorizontalAlignemt','VerticalAlignment'}
+for m = 1:num
+
+ fmt = Formats(m);
+ ext = cell2mat(get(hCtrls(m,[2 3]),'Extent'));
+ if istext(m) % text type does not use uicontrol
+ if autosize(m,1) % if autosize, minimum size to be the square
+ ext(1,3) = ext(1,4);
+ end
+ pos = [0 0 0 0];
+ else
+ pos = get(hCtrls(m,1),'Position');
+ if isbtngrp(m)
+ pos(1) = pos(1) + 1;
+ end
+ end
+ wd = [ext(1,3);pos(3);ext(2,3)];
+ ht = [ext(1,4);pos(4);ext(2,4)];
+ x0 = zeros(2,1); x = x0;
+ y0 = zeros(2,1); y = y0;
+
+ % place prompt label w.r.t. the control @ (0,0)
+ tok = regexp(fmt.labelloc,'^(left|top)(.+)$','tokens','once');
+ labelalign(m,1) = strcmp(tok{1},'left');
+ if labelalign(m,1) % left
+ % if control height is autoadjusted, make sure it is higher than label height
+ if autosize(m,2)
+ ht(2) = max(ht(2),ht(1));
+ end
+
+ alignprops{1,1} = 'left'; alignprops(1,2) = tok(2);
+ labelalign(m,2) = find(strcmp(tok{2},{'bottom','middle','top'}))-1;
+ x0(1) = -wd(1) - fmt.margin(1); % move label to left
+ x(1) = x0(1);
+ y0(1) = (ht(2)-ht(1))*labelalign(m,2)/2;
+ y(1) = ht(2)*labelalign(m,2)/2;
+
+ else % above
+ % if control width is autoadjusted, make sure it is wider than label width
+ if autosize(m,1)
+ wd(2) = max(wd(2),wd(1));
+ end
+
+ alignprops{1,2} = 'bottom'; alignprops(1,1) = tok(2);
+ labelalign(m,2) = find(strcmp(tok{2},{'left','center','right'}))-1;
+ x0(1) = (wd(2)-wd(1))*labelalign(m,2)/2;
+ x(1) = wd(2)*labelalign(m,2)/2;
+ y0(1) = ht(2) + fmt.margin(1); % move label up
+ y(1) = y0(1);
+ end
+
+ % units label w.r.t. the control @ (0,0)
+ tok = regexp(fmt.unitsloc,'^(right|bottom)(.+)$','tokens','once');
+ labelalign(m,3) = strcmp(tok{1},'right');
+ if labelalign(m,3) % right
+ % if control height is autoadjusted, make sure it is higher than unit height
+ if autosize(m,2)
+ ht(2) = max(ht(2),ht(3));
+ end
+
+ alignprops{2,1} = 'left'; alignprops(2,2) = tok(2);
+ labelalign(m,4) = find(strcmp(tok{2},{'bottom','middle','top'}))-1;
+ x0(2) = wd(2) + fmt.margin(2); % move label right
+ x(2) = x0(2);
+ y0(2) = (ht(2)-ht(3))*labelalign(m,4)/2;
+ y(2) = ht(2)*labelalign(m,4)/2;
+ else % below
+ % if control width is autoadjusted, make sure it is wider than unit width
+ if autosize(m,1)
+ wd(2) = max(wd(2),wd(3));
+ end
+
+ alignprops{2,2} = 'top'; alignprops(2,1) = tok(2);
+ labelalign(m,4) = find(strcmp(tok{2},{'left','center','right'}))-1;
+ x0(2) = (wd(2)-wd(3))*labelalign(m,4)/2;
+ x(2) = wd(2)*labelalign(m,4)/2;
+ y0(2) = -ht(3)-fmt.margin(2); % move label down
+ y(2) = -fmt.margin(2);
+ end
+
+ % translate so that all coordinates are on the first quadrant
+ xmin = min(min(x0),0);
+ ymin = min(min(y0),0);
+
+ labelbase(m,1,:) = x0 - xmin;
+ labelbase(m,2,:) = y0 - ymin;
+ labelpos(m,1,:) = x - xmin;
+ labelpos(m,2,:) = y - ymin;
+ labelsiz(m,:,:) = ext(:,[3 4]).';
+
+ ctrlpos(m,:) = [-xmin -ymin wd(2) ht(2)];
+ if isbtngrp(m)
+ ctrlpos(m,1) = ctrlpos(m,1) + 1;
+ ctrlpos(m,3) = ctrlpos(m,3) - 1;
+ end
+
+ if ~istext(m)
+ set(hCtrls(m,[2 3]),{'HorizontalAlignment','VerticalAlignment'},alignprops);
+ end
+end
+set(hCtrls(istext,2),'Units','normalized');
+
+% minimum panel size
+pnpos = [zeros(num,2) max(max(labelbase+labelsiz,[],3),ctrlpos(:,[1 2])+ctrlpos(:,[3 4]))];
+
+% if strcmpi(Options.AutoLayout,'on')
+% % minimum panel size
+% pnpos = [zeros(num,2) max(max(labelbase+labelsiz,[],3),ctrlpos(:,[1 2])+ctrlpos(:,[3 4]))];
+% map = autolayout(pnpos,Options.DesiredFigureWidth,sep);
+% dim = size(map);
+% end
+
+% Optionally align adjacent controls in each column
+if strcmpi(Options.AlignControls, 'on')
+ for n = 1:dim(2) % for each column
+ if n==1
+ notspanned = true(dim(1),1);
+ else
+ notspanned = diff(map(:,[n-1 n]),[],2)~=0;
+ end
+ idx = setdiff(unique(map(notspanned,n)),0); % must be a control & remove duplicates
+ x0 = max(ctrlpos(idx,1));
+ dx = x0-ctrlpos(idx,1);
+ ctrlpos(idx,1) = ctrlpos(idx,1) + dx;
+ labelpos(idx,1,:) = bsxfun(@plus,labelpos(idx,1,:),dx);
+ labelbase(idx,1,:) = bsxfun(@plus,labelbase(idx,1,:),dx);
+ end
+
+ % recompute the minimum panel sizes
+ pnpos = [zeros(num,2) max(max(labelbase+labelsiz,[],3),ctrlpos(:,[1 2])+ctrlpos(:,[3 4]))];
+end
+
+% position the controls w/in their respective panel, and set panel size
+nottext = ~istext;
+Nnottext = sum(nottext);
+set(hCtrls(nottext,1),{'Position'},mat2cell(ctrlpos(nottext,:),ones(Nnottext,1),4));
+set(hCtrls(nottext,2),{'Position'},mat2cell(labelpos(nottext,:,1),ones(Nnottext,1),2));
+set(hCtrls(:,3),{'Position'},mat2cell(labelpos(:,:,2),ones(num,1),2));
+set(hPanels(:,1),{'Position'},mat2cell(pnpos,ones(num,1),4));
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Determine the maximum and minimum column widths & row heights
+
+idx = map>0;
+wctrl = (pnpos(:,3)-sep*(colspan-1))./colspan;
+wcell(idx) = wctrl(map(idx));
+hctrl = (pnpos(:,4)-sep*(rowspan-1))./rowspan; % heights of controls
+hcell(idx) = hctrl(map(idx)); % -> heights of cells
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Determine which columns & rows to autosize. Only column or row is
+% autosized for spanned controls, favoring the left-most column or
+% bottom-most row
+
+% get preferred column to autosize
+% Nauto = autosize(:,1);
+Nauto = false(dim);
+Nauto(idx) = autosize(map(idx),1);
+Nauto = sum(Nauto,1); % number of autowidth controls in each column
+[~,col] = sort(fliplr(Nauto),'descend');
+col(:) = dim(2)-col+1; % preferred order of columns to be autosized
+
+% assign which column to autosize for each control
+Ictrl = find(autosize(:,1)>0); % controls with auto-sized width
+for m = Ictrl.' % for each control
+ [~,j] = find(map==m);
+ [tf,I] = ismember(col,j);
+ autowidth(j(I(find(tf,1)))) = true;
+end
+
+% get preferred row to autosize
+Nauto = false(dim);
+Nauto(idx) = autosize(map(idx),2);
+Nauto = sum(Nauto,2); % number of autowidth controls in each column
+[~,row] = sort(flipud(Nauto),'descend');
+row(:) = dim(1)-row+1; % preferred order of columns to be autosized
+
+% assign which row to autosize for each control
+Ictrl = find(autosize(:,2)>0&~istext);
+for m = Ictrl.'
+ [i,~] = find(map==m);
+ [tf,I] = ismember(row,i);
+ autoheight(i(I(find(tf,1)))) = true;
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Create the button panel
+
+% All buttons on a uipanel
+hBtnPanel = uipanel('Parent',fig,props.uipanel{:});
+
+% OK Button
+hBtn(1) = uicontrol(hBtnPanel, props.pushbutton{:}, ...
+ 'String', Options.ButtonNames{1}, 'UserData', 'OK');
+
+editable = ~all(istext);
+
+% Cancel Button
+if (Options.CancelGiven && strcmpi(Options.CancelButton,'on')) || (~Options.CancelGiven && editable)
+ hBtn(2) = uicontrol(hBtnPanel, props.pushbutton{:}, ...
+ 'String', Options.ButtonNames{2}, 'UserData', 'Cancel');
+end
+
+% Apply Button
+if strcmpi(Options.ApplyButton,'on')
+ hBtn(end+1) = uicontrol(hBtnPanel, props.pushbutton{:}, ...
+ 'String', Options.ButtonNames{3}, 'UserData', 'Apply');
+end
+
+% set size
+offset = 25;
+minwidth = 37;
+Nbtns = numel(hBtn);
+ext = cell2mat(get(hBtn,{'Extent'}));
+btnw = max(minwidth,max(ext(:,3)))+offset;
+btnh = max(ext(:,4)) + 6;
+btnsize = repmat([btnw btnh],Nbtns,1);
+btnpos = [(0:Nbtns-1).'*(btnw+sep)+sep repmat(sep,Nbtns,1)];
+
+set(hBtn,{'Position'},mat2cell([btnpos btnsize],ones(Nbtns,1),4));
+btnpanelsiz = [Nbtns*(btnw+sep)+sep btnh+2*sep];
+set(hBtnPanel,'Position',[0 0 btnpanelsiz]);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% output handle struct
+handles.fig = fig;
+handles.panels = hPanels;
+handles.ctrls = hCtrls;
+handles.btnpanel = hBtnPanel;
+handles.btns = hBtn;
+
+% output positioning info
+sinfo.map = map; % Formats grid mapping
+sinfo.istext = istext; % true if static text
+sinfo.pnminsiz = pnpos(:,[3 4]); % minimum panel size
+sinfo.ctrlpos = ctrlpos; % control position w/in panel
+sinfo.labelpos = labelpos; % label positions w/in panel
+sinfo.labelalign = labelalign; % label alignments
+sinfo.btnpnsiz = btnpanelsiz; % button panel size
+
+sinfo.w_max = max(wcell,[],1);
+sinfo.w_min = min(wcell,[],1);
+sinfo.h_max = max(hcell,[],2);
+sinfo.h_min = min(hcell,[],2);
+
+sinfo.w_delta = sinfo.w_max-sinfo.w_min;
+sinfo.h_delta = sinfo.h_max-sinfo.h_min;
+
+% total non-adjustable width & height over all controls
+sinfo.w_totalnonadj = sum(sinfo.w_max(~autowidth))+sum(sinfo.w_min(autowidth));
+sinfo.h_totalnonadj = sum(sinfo.h_max(~autoheight))+sum(sinfo.h_min(autoheight));
+
+% autosize related fields
+sinfo.autosize = logical(autosize); % 1 to autosize once, 2 to resize with window
+sinfo.autowidth = find(autowidth);
+sinfo.autoheight = find(autoheight);
+
+figw = max(sum(sinfo.w_max)+sep*(dim(2)+1),sinfo.btnpnsiz(1)+2*sep);
+figh = sum(sinfo.h_max)+sep*(dim(1)+1)+sinfo.btnpnsiz(2);
+sinfo.figsizmin = min([figw figh], Options.DialogSizeMinimum);
+
+% Set default figure size
+Nauto = [numel(sinfo.autowidth) numel(sinfo.autoheight)];
+figpos = get(fig,'Position');
+if Nauto(1)==0 % all fixed columns
+ figpos(3) = sinfo.figsizmin(1);
+else
+ figpos(3) = max(figpos(3),sinfo.figsizmin(1)+59*Nauto(1));
+end
+if Nauto(2)==0 % all fixed rows
+ figpos(4) = sinfo.figsizmin(2);
+else
+ figpos(4) = max(figpos(4),sinfo.figsizmin(2)+19*Nauto(2));
+end
+set(fig,'Position',figpos);
+movegui(fig,'onscreen');
+
+end
+
+function resizegui(handles,sinfo,Options)
+% This function places all controls in proper place.
+% Must be called before the GUI is made visible as buildgui function
+% just creates uicontrols and do not place them in proper places.
+
+% KNOWN ISSUE: Resize event does not fire all the time
+
+sep = Options.Sep; % separation between controls
+
+% get current figure size
+figPos = get(handles.fig,'Position');
+figSize = figPos(3:4);
+
+% force figure size to be larger than the minimum size allowed
+idx = figSize1
+ % send last word off to the next line
+ [idx,word] = regexp(str,'\s(\S+)$','start','tokens','once');
+ str(idx:end) = [];
+ set(handles.ctrls(m,2),'String',str);
+ ext = get(handles.ctrls(m,2),'Extent');
+ if k==Nmsgs && numel(msg)==Nmsgs
+ msg(k+1) = word;
+ else
+ msg{k+1} = sprintf('%s %s',word{1},msg{k+1});
+ end
+ end
+ end
+
+ % set height
+ i = unique(i);
+ heights(i) = max(heights(i),ext(4)/numel(i));
+ h_min(i) = min(h_min(i),ext(4)/numel(i));
+end
+
+% Determine the automatically adjusted row heights
+h_total = figSize(2)-sep*(dim(1)+1)-sinfo.btnpnsiz(2);
+autoheight = sinfo.autoheight;
+totalnonadj = sinfo.h_totalnonadj;
+h_adj = (h_total-totalnonadj)/numel(autoheight);
+heights(autoheight) = h_min(autoheight) + h_adj;
+
+idx = heights(autoheight)