function [hPropsPane,parameters] = propertiesGUI(hParent, parameters)
% propertiesGUI displays formatted editable list of properties
%
% Syntax:
% [hPropsPane,parameters] = propertiesGUI(hParent, parameters)
%
% Description:
% propertiesGUI processes a list of data properties and displays
% them in a GUI table, where each parameter value has a unique
% associated editor.
%
% propertiesGUI by itself, with no input parameters, displays a demo
%
% By default, propertiesGUI identifies and processes the following
% field types: signed, unsigned, float, file, folder, text or string,
% color, IPAddress, password, date, boolean, cell-array, numeric array,
% class object.
%
% Inputs:
% hParent - optional handle of a parent GUI container (figure/uipanel
% /uitab) in which the properties table will appear.
% If missing or empty or 0, the table will be shown in a
% new modal dialog window; otherwise it will be embedded
% in the parent container.
%
% parameters - struct or object with data fields. The fields are
% processed separately to determine their corresponding cell
% editor. If parameters is not specified, then the global
% test_data will be used. If test_data is also empty, then
% a demo of several different data types will be used.
%
% Outputs:
% hPropsPane - handle of the properties panel widget, which can be
% customized to display field descriptions, toolbar, etc.
%
% parameters - the resulting (possibly-updated) parameters struct.
% Naturally, this is only relevant in case of a modal dialog.
%
% (global test_data) - this global variable is updated internally when
% the <OK> button is clicked. It is meant to enable easy data
% passing between the properties GUI and other application
% component. Using global vars is generally discouraged as
% bad programming, but it simplifies component interaction.
%
% Customization:
% This utility is meant to be used either as stand-alone, or as a
% template for customization. For example, you can attach a unique
% description to each property that will be shown in an internal
% sub-panel: see the customizePropertyPane() and preparePropsList()
% sub-functions.
%
% When passing the properties in an input parameters struct, the
% utility automatically inspects each struct field and assigns a
% corresponding cell-editor with no description and a field label
% that reflects the field name. The properties are automatically
% set as modifiable (editable) and assigned a default callback
% function (propUpdatedCallback() sub-function).
% See the demoParameters() sub-function for some examples.
%
% You can have specific control over each property's description,
% label, editability, cell-editor and callback function. See the
% preparePropsList() sub-functions for some examples. You can add
% additional cell-editors/renderers in the newProperty() sub-function.
%
% You can place specific control over the acceptable property values
% by entering custom code into the checkProp() sub-function.
%
% Future development:
% 1. Fix the renderer/editor for numeric and cell arrays
% 2. Enable more control over appearance and functionality via
% propertiesGUI's input parameters
% 3. Add additional built-in cell editors/renderers: time, slider,
% point, rectangle (=position), font, ...
%
% Example:
% propertiesGUI; % displays the demo
%
% params.name = 'Yair';
% params.age = uint8(41);
% params.folder = pwd;
% params.date = now;
% params.size.width = 10;
% params.size.height = 20;
% [hPropsPane, params] = propertiesGUI(params);
%
% Bugs and suggestions:
% Please send to Yair Altman (altmany at gmail dot com)
%
% Warning:
% This code heavily relies on undocumented and unsupported Matlab
% functionality. It works on Matlab 7+, but use at your own risk!
%
% A technical description of the implementation can be found at:
% http://undocumentedmatlab.com/blog/propertiesGUI/
% http://undocumentedmatlab.com/blog/jide-property-grids/
% http://undocumentedmatlab.com/blog/advanced-jide-property-grids/
%
% Change log:
% 2013-04-23: Fixed case of empty ([]) data, handled class objects & numeric/cell arrays, fixed error reported by Andrew Ness
% 2013-01-26: Updated help section
% 2012-11-07: Minor fix for file/folder properties
% 2012-11-07: Accept any object having properties/fields as input parameter; support multi-level properties
% 2012-10-31: First version posted on <a href="http://www.mathworks.com/matlabcentral/fileexchange/authors/27420">MathWorks File Exchange</a>
%
% See also:
% inspect, uiinspect (#17935 on the MathWorks File Exchange)
% License to use and modify this code is granted freely to all interested, as long as the original author is
% referenced and attributed as such. The original author maintains the right to be solely associated with this work.
% Programmed and Copyright by Yair M. Altman: altmany(at)gmail.com
% $Revision: 1.07 $ $Date: 2013/04/23 00:15:42 $
% Get the initial data
global test_data
if nargin < 2
try
isObj = nargin==1;
[hasProps,isHG] = hasProperties(hParent);
isObj = isObj && hasProps && ~isHG;
catch
% ignore - maybe nargin==0, so no hParent is available
end
if isObj
parameters = hParent;
hParent = [];
else
parameters = test_data; % comment this if you do not want persistent parameters
if isempty(parameters)
% demo mode
parameters = demoParameters;
end
end
end
% Accept any object having data fields/properties
try
parameters = get(parameters);
catch
oldWarn = warning('off','MATLAB:structOnObject');
parameters = struct(parameters);
warning(oldWarn);
end
% Init JIDE
com.mathworks.mwswing.MJUtilities.initJIDE;
% Prepare the list of properties
oldWarn = warning('off','MATLAB:hg:JavaSetHGProperty');
warning off MATLAB:hg:PossibleDeprecatedJavaSetHGProperty
isEditable = true; %=nargin < 1;
propsList = preparePropsList(parameters, isEditable);
% Create a mapping propName => prop
propsHash = java.util.Hashtable;
propsArray = propsList.toArray();
for propsIdx = 1 : length(propsArray)
thisProp = propsArray(propsIdx);
propName = get(thisProp, 'UserData');
propsHash.put(propName, thisProp);
end
warning(oldWarn);
% Prepare a properties table that contains the list of properties
model = javaObjectEDT(com.jidesoft.grid.PropertyTableModel(propsList));
model.expandAll();
% Prepare the properties table (grid)
grid = javaObjectEDT(com.jidesoft.grid.PropertyTable(model));
grid.setShowNonEditable(grid.SHOW_NONEDITABLE_BOTH_NAME_VALUE);
%set(handle(grid.getSelectionModel,'CallbackProperties'), 'ValueChangedCallback', @propSelectedCallback);
com.jidesoft.grid.TableUtils.autoResizeAllColumns(grid);
%com.jidesoft.grid.TableUtils.autoResizeAllRows(grid);
grid.setRowHeight(19); % default=16; autoResizeAllRows=20 - we need something in between
% Auto-end editing upon focus loss
grid.putClientProperty('terminateEditOnFocusLost',true);
% If no parent (or the root) was specified
if nargin < 1 || isempty(hParent) || isequal(hParent,0)
% Create a new figure window
delete(findall(0, '-depth',1, 'Tag','fpropertiesGUI'));
hFig = figure('Number','off', 'Name','Application properties', 'Units','pixel', 'Pos',[300,200,500,500], 'Menu','none', 'Toolbar','none', 'Tag','fpropertiesGUI', 'Visible','off');
hParent = hFig;
setappdata(0,'isParamsGUIApproved',false)
% Add the bottom action buttons
btOK = uicontrol('String','OK', 'Units','pixel', 'Pos',[ 50,5,60,30], 'Tag','btOK', 'Callback',@btOK_Callback);
btCancel = uicontrol('String','Cancel', 'Units','pixel', 'Pos',[150,5,60,30], 'Tag','btCancel', 'Callback',@(h,e)close(hFig)); %#ok<NASGU>
% Check the property values to determine whether the <OK> button should be enabled or not
checkProps(propsList, btOK, true);
% Set the figure icon & make visible
jFrame = get(handle(hFig),'JavaFrame');
icon = javax.swing.ImageIcon(fullfile(matlabroot, '/toolbox/matlab/icons/tool_legend.gif'));
jFrame.setFigureIcon(icon);
set(hFig, 'WindowStyle','modal', 'Visible','on');
% Set the component's position
%pos = [5,40,490,440];
hFigPos = getpixelposition(hFig);
pos = [5,40,hFigPos(3)-10,hFigPos(4)-50];
else
% Set the component's position
drawnow;
pos = getpixelposition(hParent);
pos(1:2) = 5;
pos = pos - [0,0,10,10];
hFig = [];
end
%drawnow; pause(0.05);
pane = javaObjectEDT(com.jidesoft.grid.PropertyPane(grid));
customizePropertyPane(pane);
[jPropsPane, hPropsPane_] = javacomponent(pane, pos, hParent);
setappdata(hParent, 'jPropsPane',jPropsPane);
setappdata(hParent, 'propsList',propsList);
setappdata(hParent, 'propsHash',propsHash);
setappdata(hParent, 'mirror',parameters);
set(hPropsPane_,'tag','hpropertiesGUI');
set(hPropsPane_, 'Units','norm');
% Align the background colors
bgcolor = pane.getBackground.getComponents([]);
try set(hParent, 'Color', bgcolor(1:3)); catch, end % this fails in uitabs - never mind (works ok in stand-alone figures)
try pane.setBorderColor(pane.getBackground); catch, end % error reported by Andrew Ness
% If a new figure was created, make it modal and wait for user to close it
if ~isempty(hFig)
uiwait(hFig);
if getappdata(0,'isParamsGUIApproved')
parameters = test_data; %=getappdata(hFig, 'mirror');
end
end
if nargout, hPropsPane = hPropsPane_; end % prevent unintentional printouts to the command window
end % propertiesGUI
%% Determine whether a specified object should be considered as having fields/properties
% Note: HG handles must be processed seperately for the main logic to work
function [hasProps,isHG] = hasProperties(object)
% A bunch of tests, some of which may croak depending on the Matlab release, platform
try isHG = ishghandle(object); catch, isHG = ishandle(object); end
try isst = isstruct(object); catch, isst = false; end
try isjav = isjava(object); catch, isjav = false; end
try isobj = isobject(object); catch, isobj = false; end
try isco = iscom(object); catch, isco = false; end
hasProps = ~isempty(object) && (isst || isjav || isobj || isco);
end
%% Customize the property-pane's appearance
function customizePropertyPane(pane)
pane.setShowDescription(false); % YMA: we don't currently have textual descriptions of the parameters, so no use showing an empty box that just takes up GUI space...
pane.setShowToolBar(false);
pane.setOrder(2); % uncategorized, unsorted - see http://undocumentedmatlab.com/blog/advanced-jide-property-grids/#comment-42057
end
%% Prepare a list of some parameters for demo mode
function parameters = demoParameters
parameters.floating_point_property = pi;
parameters.signed_integer_property = int16(12);
parameters.unsigned_integer_property = uint16(12);
parameters.flag_property = true;
parameters.file_property = mfilename('fullpath');
parameters.folder_property = pwd;
parameters.text_property = 'Sample text';
parameters.fixed_choice_property = {'Yes','No','Maybe'};
parameters.editable_choice_property = {'Yes','No','Maybe',''}; % editable if the last cell element is ''
parameters.date_property = java.util.Date; % today's date
parameters.another_date_property = now-365; % last year
parameters.time_property = datestr(now,'HH:MM:SS');
parameters.password_property = '*****';
parameters.IP_address_property = '10.20.30.40';
parameters.my_category.width = 4;
parameters.my_category.height = 3;
parameters.my_category.and_a_subcategory.is_OK = true;
parameters.numeric_array_property = [11,12,13,14];
parameters.cell_array_property = {1,magic(3),'text',-4};
parameters.color_property = [0.4,0.5,0.6];
parameters.another_color_property = java.awt.Color.red;
try parameters.class_object_property = matlab.desktop.editor.getActive; catch, end
end % demoParameters
%% Prepare a list of properties
function propsList = preparePropsList(parameters, isEditable)
propsList = java.util.ArrayList();
% Convert a class object into a struct
if isobject(parameters)
parameters = struct(parameters);
end
% Prepare a dynamic list of properties, based on the struct fields
if isstruct(parameters) && ~isempty(parameters)
% Dynamically (generically) inspect all the fields and assign corresponding props
field_names = fieldnames(parameters);
for field_idx = 1 : length(field_names)
field_name = field_names{field_idx};
value = parameters.(field_name);
field_label = strrep(field_name,'_',' ');
field_label(1) = upper(field_label(1));
field_description = ''; % TODO
type = 'string';
if isempty(value)
type = 'string'; % not really needed, but for consistency
elseif isa(value,'java.awt.Color')
type = 'color';
elseif isnumeric(value)
try %if length(value)==3
colorComponents = num2cell(value);
if numel(colorComponents) ~= 3
error(' '); % bail out if definitely not a color
end
try
value = java.awt.Color(colorComponents{:}); % value between 0-1
catch
colorComponents = num2cell(value/255);
value = java.awt.Color(colorComponents{:}); % value between 0-255
end
type = 'color';
catch %else
if numel(value)==1
%value = value(1);
if value > now-3650 && value < now+3650
type = 'date';
value = java.util.Date(datestr(value));
elseif isa(value,'uint') || isa(value,'uint8') || isa(value,'uint16') || isa(value,'uint32') || isa(value,'uint64')
type = 'unsigned';
elseif isinteger(value)
type = 'signed';
else
type = 'float';
end
else
value = strtrim(regexprep(num2str(value),' +',' '));
if length(value) > 50
value(51:end) = '';
value = [value '...'];
end
value = ['[ ' value ' ]'];
end
end
elseif islogical(value)
type = 'boolean';
elseif ischar(value)
if exist(value,'dir')
type = 'folder';
value = java.io.File(value);
elseif exist(value,'file')
type = 'file';
value = java.io.File(value);
elseif value(1)=='*'
type = 'password';
elseif sum(value=='.')==3
type = 'IPAddress';
else
type = 'string';
if length(value) > 50
value(51:end) = '';
value = [value '...'];
end
end
elseif iscellstr(value)
type = value; % editable if the last cell element is ''
elseif isa(value,'java.util.Date')
type = 'date';
elseif isa(value,'java.io.File')
if value.isFile
type = 'file';
else % value.isDirectory
type = 'folder';
end
elseif iscell(value)
value = strtrim(regexprep(evalc('disp(value)'),' +',' '));
value = ['{ ' value ' }'];
elseif isobject(value)
oldWarn = warning('off','MATLAB:structOnObject');
value = struct(value);
warning(oldWarn);
elseif ~isstruct(value)
value = strtrim(regexprep(evalc('disp(value)'),' +',' '));
end
parameters.(field_name) = value; % possibly updated above
newProp = newProperty(parameters, field_name, field_label, isEditable, type, field_description, @propUpdatedCallback);
propsList.add(newProp);
end
else
% You can also use direct assignments, instead of the generic code above. For example:
% (Possible property types: signed, unsigned, float, file, folder, text or string, color, IPAddress, password, date, boolean, cell-array of strings)
propsList.add(newProperty(parameters, 'flag_prop_name', 'Flag value:', isEditable, 'boolean', 'Turn this on if you want to make extra plots', @propUpdatedCallback));
propsList.add(newProperty(parameters, 'float_prop_name', 'Boolean prop', isEditable, 'float', 'description 123...', @propUpdatedCallback));
propsList.add(newProperty(parameters, 'string_prop_name', 'My text msg:', isEditable, 'string', 'Yaba daba doo', @propUpdatedCallback));
propsList.add(newProperty(parameters, 'int_prop_name', 'Now an integer', isEditable, 'unsigned', '123 456...', @propUpdatedCallback));
propsList.add(newProperty(parameters, 'choice_prop_name', 'And a drop-down', isEditable, {'Yes','No','Maybe'}, 'no description here!', @propUpdatedCallback));
end
end % preparePropsList
%% Prepare a data property
function prop = newProperty(dataStruct, propName, label, isEditable, dataType, description, propUpdatedCallback)
% Auto-generate the label from the property name, if the label was not specified
if isempty(label)
label = strrep(propName,'_',' ');
label(1) = upper(label(1));
end
% Create a new property with the chosen label
prop = javaObjectEDT(com.jidesoft.grid.DefaultProperty); % UNDOCUMENTED internal MATLAB component
prop.setName(label);
% Set the property to the current patient's data value
thisProp = dataStruct.(propName);
if isstruct(thisProp) %hasProperties(thisProp)
% Accept any object having data fields/properties
try
thisProp = get(thisProp);
catch
oldWarn = warning('off','MATLAB:structOnObject');
thisProp = struct(thisProp);
warning(oldWarn);
end
% Parse the children props and add them to this property
summary = regexprep(evalc('disp(thisProp)'),' +',' ');
prop.setValue(summary); % TODO: display summary dynamically
prop.setValue('');
prop.setEditable(false);
children = toArray(preparePropsList(thisProp, isEditable));
for childIdx = 1 : length(children)
prop.addChild(children(childIdx));
end
else
prop.setValue(thisProp);
prop.setEditable(isEditable);
end
set(prop,'UserData',propName);
% Set property editor, renderer and alignment
if iscell(dataType)
% treat this as drop-down values
cbIsEditable = true;
if isempty(dataType{end}) % ends with '' - editable
dataType(end) = []; % remove from the drop-down list
else % standard drop-down, non-editable
cbIsEditable = false;
end
editor = com.jidesoft.grid.ListComboBoxCellEditor(dataType);
try editor.getComboBox.setEditable(cbIsEditable); catch, end %#ok<NOCOM>
%set(editor,'EditingStoppedCallback',{@propUpdatedCallback,tagName,propName});
alignProp(prop, editor);
try prop.setValue(dataStruct.(propName){1}); catch, end
else
switch lower(dataType)
case 'signed', %alignProp(prop, com.jidesoft.grid.IntegerCellEditor, 'int32');
model = javax.swing.SpinnerNumberModel(prop.getValue, -intmax, intmax, 1);
editor = com.jidesoft.grid.SpinnerCellEditor(model);
alignProp(prop, editor, 'int32');
case 'unsigned', %alignProp(prop, com.jidesoft.grid.IntegerCellEditor, 'uint32');
model = javax.swing.SpinnerNumberModel(prop.getValue, 0, intmax, 1);
editor = com.jidesoft.grid.SpinnerCellEditor(model);
alignProp(prop, editor, 'uint32');
case 'float', alignProp(prop, com.jidesoft.grid.CalculatorCellEditor, 'double'); % DoubleCellEditor
case 'boolean', alignProp(prop, com.jidesoft.grid.BooleanCheckBoxCellEditor, 'logical');
case 'folder', alignProp(prop, com.jidesoft.grid.FolderCellEditor);
case 'file', alignProp(prop, com.jidesoft.grid.FileCellEditor);
case 'ipaddress', alignProp(prop, com.jidesoft.grid.IPAddressCellEditor);
case 'password', alignProp(prop, com.jidesoft.grid.PasswordCellEditor);
case 'color', alignProp(prop, com.jidesoft.grid.ColorCellEditor);
case 'text', alignProp(prop);
case 'time', alignProp(prop); % maybe use com.jidesoft.grid.FormattedTextFieldCellEditor ?
case 'date', dateModel = com.jidesoft.combobox.DefaultDateModel;
dateFormat = java.text.SimpleDateFormat('dd/MM/yyyy');
dateModel.setDateFormat(dateFormat);
editor = com.jidesoft.grid.DateCellEditor(dateModel, 1);
alignProp(prop, editor, 'java.util.Date');
try
prop.setValue(dateFormat.parse(prop.getValue)); % convert string => Date
catch
% ignore
end
otherwise, alignProp(prop); % treat as a simple text field
end
end % for all possible data types
prop.setDescription(description);
if ~isempty(description)
renderer = com.jidesoft.grid.CellRendererManager.getRenderer(prop.getType, prop.getEditorContext);
renderer.setToolTipText(description);
end
% Set the property's editability state
if prop.isEditable
% Set the property's label to be black
prop.setDisplayName(['<html><font size="4" color="black">' label]);
% Add callbacks for property-change events
hprop = handle(prop, 'CallbackProperties');
set(hprop,'PropertyChangeCallback',{propUpdatedCallback,propName});
else
% Set the property's label to be gray
prop.setDisplayName(['<html><font size="4" color="gray">' label]);
end
end % newProperty
%% Align a text property to right/left
function alignProp(prop, editor, propTypeStr, direction)
if nargin < 2, editor = com.jidesoft.grid.StringCellEditor; end %(javaclass('char',1));
if nargin < 3, propTypeStr = 'cellstr'; end % => javaclass('char',1)
if nargin < 4, direction = javax.swing.SwingConstants.RIGHT; end
% Set this property's data type
propType = javaclass(propTypeStr);
prop.setType(propType);
% Prepare a specific context object for this property
if strcmpi(propTypeStr,'logical')
%TODO - FIXME
context = editor.CONTEXT;
prop.setEditorContext(context);
%renderer = CheckBoxRenderer;
%renderer.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
%com.jidesoft.grid.CellRendererManager.registerRenderer(propType, renderer, context);
else
context = com.jidesoft.grid.EditorContext(prop.getName);
prop.setEditorContext(context);
% Register a unique cell renderer so that each property can be modified seperately
%renderer = com.jidesoft.grid.CellRendererManager.getRenderer(propType, prop.getEditorContext);
renderer = com.jidesoft.grid.ContextSensitiveCellRenderer;
com.jidesoft.grid.CellRendererManager.registerRenderer(propType, renderer, context);
renderer.setBackground(java.awt.Color.white);
renderer.setHorizontalAlignment(direction);
%renderer.setHorizontalTextPosition(direction);
end
% Update the property's cell editor
try editor.setHorizontalAlignment(direction); catch, end
try
editor.getTextField.setHorizontalAlignment(direction);
catch
try
editor.getComboBox.setHorizontalAlignment(direction);
catch
% ignore
end
end
% Set limits on unsigned int values
try
if strcmpi(propTypeStr,'uint32')
%pause(0.01);
editor.setMinInclusive(java.lang.Integer(0));
editor.setMinExclusive(java.lang.Integer(-1));
editor.setMaxExclusive(java.lang.Integer(intmax));
editor.setMaxInclusive(java.lang.Integer(intmax));
end
catch
% ignore
end
com.jidesoft.grid.CellEditorManager.registerEditor(propType, editor, context);
end % alignProp
%% Property updated callback function
function propUpdatedCallback(prop, eventData, propName)
try if strcmpi(char(eventData.getPropertyName),'parent'), return; end; catch, end
hFig = findall(0, '-depth',1, 'Tag','fpropertiesGUI');
if isempty(hFig)
hPropsPane = findall(0,'Tag','hpropertiesGUI');
if isempty(hPropsPane), return; end
hFig = get(hPropsPane,'Parent');
end
if isempty(hFig), return; end
propsList = getappdata(hFig, 'propsList');
propsPane = getappdata(hFig, 'jPropsPane');
data = getappdata(hFig, 'mirror');
% Get the updated property value
propValue = get(prop,'Value'); %#ok<NASGU> (used in eval() below)
if isjava(propValue)
if isa(propValue,'java.util.Date')
sdf = java.text.SimpleDateFormat('MM-dd-yyyy');
propValue = datenum(sdf.format(propValue).char); %#ok<NASGU>
elseif isa(propValue,'java.awt.Color')
propValue = propValue.getColorComponents([])'; %#ok<NASGU>
else
propValue = char(propValue); %#ok<NASGU>
end
end
% Get the actual recursive propName
try
oldWarn = warning('off','MATLAB:hg:JavaSetHGProperty');
try prop = java(prop); catch, end
while isa(prop,'com.jidesoft.grid.Property')
prop = get(prop,'Parent');
newName = get(prop,'UserData');
if isempty(newName), break; end
propName = [newName '.' propName]; %#ok<AGROW>
end
catch
% Reached the top of the property's heirarchy - bail out
warning(oldWarn);
end
% Update the mirror with the updated field value
%data.(propName) = propValue; % croaks on multiple sub-fields
eval(['data.' propName ' = propValue;']);
% Update the local mirror
setappdata(hFig, 'mirror',data);
% Update the display
checkProps(propsList, hFig);
try propsPane.repaint; catch; end
end % propUpdatedCallback
%% <OK> button callback function
function btOK_Callback(btOK, eventData) %#ok<INUSD>
global test_data
% Store the current data-info struct mirror in the global struct
hFig = ancestor(btOK, 'figure');
test_data = getappdata(hFig, 'mirror');
setappdata(0,'isParamsGUIApproved',true);
% Close the window
try
close(hFig);
catch
delete(hFig); % force-close
end
end % btOK_Callback
%% Check whether all mandatory fields have been filled, update background color accordingly
function checkProps(propsList, hContainer, isInit)
if nargin < 3, isInit = false; end
okEnabled = 'on';
try propsArray = propsList.toArray(); catch, return; end
for propsIdx = 1 : length(propsArray)
isOk = checkProp(propsArray(propsIdx));
if ~isOk || isInit, okEnabled = 'off'; end
end
% Update the <OK> button's editability state accordingly
btOK = findall(hContainer, 'Tag','btOK');
set(btOK, 'Enable',okEnabled);
drawnow; pause(0.01);
end % checkProps
function isOk = checkProp(prop)
isOk = true;
oldWarn = warning('off','MATLAB:hg:JavaSetHGProperty');
warning off MATLAB:hg:PossibleDeprecatedJavaSetHGProperty
propName = get(prop, 'UserData');
renderer = com.jidesoft.grid.CellRendererManager.getRenderer(get(prop,'Type'), get(prop,'EditorContext'));
warning(oldWarn);
mandatoryFields = {}; % TODO - add the mandatory field-names here
if any(strcmpi(propName, mandatoryFields)) && isempty(get(prop,'Value'))
propColor = java.awt.Color.yellow;
isOk = false;
elseif ~prop.isEditable
%propColor = java.awt.Color.gray;
propColor = renderer.getBackground();
else
propColor = java.awt.Color.white;
end
renderer.setBackground(propColor);
end % checkProp
%% Return java.lang.Class instance corresponding to the Matlab type
function jclass = javaclass(mtype, ndims)
% Input arguments:
% mtype:
% the MatLab name of the type for which to return the java.lang.Class
% instance
% ndims:
% the number of dimensions of the MatLab data type
%
% See also: class
% Copyright 2009-2010 Levente Hunyadi
% Downloaded from: http://www.UndocumentedMatlab.com/files/javaclass.m
validateattributes(mtype, {'char'}, {'nonempty','row'});
if nargin < 2
ndims = 0;
else
validateattributes(ndims, {'numeric'}, {'nonnegative','integer','scalar'});
end
if ndims == 1 && strcmp(mtype, 'char'); % a character vector converts into a string
jclassname = 'java.lang.String';
elseif ndims > 0
jclassname = javaarrayclass(mtype, ndims);
else
% The static property .class applied to a Java type returns a string in
% MatLab rather than an instance of java.lang.Class. For this reason,
% use a string and java.lang.Class.forName to instantiate a
% java.lang.Class object; the syntax java.lang.Boolean.class will not do so
switch mtype
case 'logical' % logical vaule (true or false)
jclassname = 'java.lang.Boolean';
case 'char' % a singe character
jclassname = 'java.lang.Character';
case {'int8','uint8'} % 8-bit signed and unsigned integer
jclassname = 'java.lang.Byte';
case {'int16','uint16'} % 16-bit signed and unsigned integer
jclassname = 'java.lang.Short';
case {'int32','uint32'} % 32-bit signed and unsigned integer
jclassname = 'java.lang.Integer';
case {'int64','uint64'} % 64-bit signed and unsigned integer
jclassname = 'java.lang.Long';
case 'single' % single-precision floating-point number
jclassname = 'java.lang.Float';
case 'double' % double-precision floating-point number
jclassname = 'java.lang.Double';
case 'cellstr' % a single cell or a character array
jclassname = 'java.lang.String';
otherwise
jclassname = mtype;
%error('java:javaclass:InvalidArgumentValue', ...
% 'MatLab type "%s" is not recognized or supported in Java.', mtype);
end
end
% Note: When querying a java.lang.Class object by name with the method
% jclass = java.lang.Class.forName(jclassname);
% MatLab generates an error. For the Class.forName method to work, MatLab
% requires class loader to be specified explicitly.
jclass = java.lang.Class.forName(jclassname, true, java.lang.Thread.currentThread().getContextClassLoader());
end % javaclass
%% Return the type qualifier for a multidimensional Java array
function jclassname = javaarrayclass(mtype, ndims)
switch mtype
case 'logical' % logical array of true and false values
jclassid = 'Z';
case 'char' % character array
jclassid = 'C';
case {'int8','uint8'} % 8-bit signed and unsigned integer array
jclassid = 'B';
case {'int16','uint16'} % 16-bit signed and unsigned integer array
jclassid = 'S';
case {'int32','uint32'} % 32-bit signed and unsigned integer array
jclassid = 'I';
case {'int64','uint64'} % 64-bit signed and unsigned integer array
jclassid = 'J';
case 'single' % single-precision floating-point number array
jclassid = 'F';
case 'double' % double-precision floating-point number array
jclassid = 'D';
case 'cellstr' % cell array of strings
jclassid = 'Ljava.lang.String;';
otherwise
jclassid = ['L' mtype ';'];
%error('java:javaclass:InvalidArgumentValue', ...
% 'MatLab type "%s" is not recognized or supported in Java.', mtype);
end
jclassname = [repmat('[',1,ndims), jclassid];
end % javaarrayclass