Skip to content

Instantly share code, notes, and snippets.

@CitizenInsane
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CitizenInsane/3ce1755bf0901f0bae38 to your computer and use it in GitHub Desktop.
Save CitizenInsane/3ce1755bf0901f0bae38 to your computer and use it in GitHub Desktop.
Graphical edition of fields (or properties) of a matlab structure (or object).
function [myNewStruct] = DemoCallback()
%[
% Define a structure to work with
myStruct.MyField1 = 'lalala';
myStruct.MyField2(2,4).Titi = 'hello';
myStruct.MyField2(2,4).Folder = 'hello';
% Edit structure
[b, myNewStruct] = uipropedit(myStruct, @editorCallback);
if (~b), myNewStruct = myStruct; end
%]
%% callback to override the default edition mode of the fields
function [editParameters] = editorCallback(path, propertyName, propertyValue, editParameters)
%[
switch(sprintf('%s.%s', path, propertyName))
case '.MyField1'
editParameters.Category = 'Correction';
editParameters.DisplayName = 'First field';
editParameters.Description = 'This is the first field.';
case '.MyField2.Titi'
editParameters.Type = { 'hello', 'world' };
editParameters.DisplayValue = { 'hello', 'world' };
editParameters.DisplayName = 'Second n-dim field';
editParameters.Description = 'Select either ''hello'' or ''world''.';
case '.MyField2.Folder'
editParameters.Type = 'folder';
editParameters.Description = 'Please select a folder.';
end
%]
%
% PURPOSE:
%
% Graphical edition of fields (or properties) of a matlab structure
% (or object). Supports also for editing non struct or non object
% variables.
%
% SYNTAX:
%
% 1) Modal display
% [b, updatedData] = uipropedit();
% [b, updatedData] = uipropedit(data);
% [b, updatedData] = uipropedit(data, callback);
%
% 2) Integration in GUI
% [hPropertyEditor, jPropertyEditor] = uipropedit(data, callback, hParent);
%
% DESCRIPTION:
%
% ** Inputs **
%
% - hParent: Graphical object that will contain the property grid editor
% object. When this argument is missing, property editor is
% displayed in a brand new modal dialog.
%
% - callback: Callback allowing to overridde the default edition settings
% of the various data fields. When this argument is missing
% no override occurs and edition is based upon the type and
% the content of the field.
%
% The syntax for callback routine is as follow:
%
% [editParameters] = editorCallback(path, propertyName, propertyValue, editParameters)
%
% - path: The path of the property (not accounting for position in case of n-d fields)
% - propertyName: The name of the field
% - propertyValue: The current value of the field
% - editParameters: default editions parameters (to override or not)
%
% It can be used like this:
%
% function [myNewStruct] = DemoCallback()
% %[
% % Define a structure to work with
% myStruct.MyField1 = 'lalala';
% myStruct.MyField2(2,4).Titi = 'hello';
% myStruct.MyField2(1,2).Folder = 'hello';
%
% % Edit structure (using a callback to select how to edit fields)
% [b, myNewStruct] = uipropedit(myStruct, @editorCallback);
% if (~b), myNewStruct = myStruct; end
% %]
%
% % Callback to override the default edition settings
% function [editParameters] = editorCallback(path, propertyName, propertyValue, editParameters)
% %[
% switch(sprintf('%s.%s', path, propertyName))
%
% case '.MyField1'
% editParameters.Category = 'Correction';
% editParameters.DisplayName = 'First field';
% editParameters.Description = 'This is the first field.';
%
% case '.MyField2.Titi'
% editParameters.Type = { 'hello', 'world' };
% editParameters.DisplayValue = { 'hello', 'world' };
% editParameters.DisplayName = 'Second n-dim field';
% editParameters.Description = 'Select either ''hello'' or ''world''.';
%
% case '.MyField2.Folder'
% editParameters.Type = 'folder';
% editParameters.Description = 'Please select a folder.';
%
% end
% %]
%
% - data: The structure or the object to display. When this argument is
% missing a default structure is provided for demonstration
% purpose.
%
% ** Outputs **
%
% - When hParent is ommited or empty, the property editor is encapsulated
% in a modal dialog an return is of the form '[b, updatedData]':
%
% * b: 'True' if user validated the modifications, 'False' otherwise
% * updatedData: The new data structure (empty if b == false)
%
% - When hParent is provided, the routine return immedialtly with a
% result of the form '[hPropertyEditor, jPropertyEditor]'
%
% * hPropertyEditor: Handle to the created editor's graphical control in matlab
% * jPropertyEditor: Handle to underlying java control ('com.jidesoft.grid.PropertyPane').
%
% REMARKS:
%
% Programmed by sissartel(at)satimo.fr
% Code is adapted from 'propertiesGUI' routine by Yair M. Altman.
%
% SEE ALSO:
%
% http://undocumentedmatlab.com/blog/propertiesGUI
% http://undocumentedmatlab.com/blog/jide-property-grids
% http://undocumentedmatlab.com/blog/advanced-jide-property-grids
%
%% --- Main function
function [varargout] = uipropedit(data, editionCallback, hParent)
%[
% Initialize JIDE's usage within Matlab
com.mathworks.mwswing.MJUtilities.initJIDE;
% Parse arguments
if (nargin < 3), hParent = []; end
if (nargin < 2), editionCallback = []; end
if (nargin < 1), data = createDummyData(); end
% Checks for returned values
isModalReturn = isempty(hParent);
if (nargout > 2), error('Too many output arguments.'); end
% Lets start by burying the edited structure/object in some other
% object in order to retreive it easily and reflect changes made the
% GUI. (Here using java object, but this could be any handle object:
% an invisble figure, a handle-class, etc...)
buriedDataContainer = java.util.Observable();
setUserDataOnJavaObject(buriedDataContainer, data);
% Prepare the list of properties
propertiesList = preparePropertiesList(buriedDataContainer, data, editionCallback);
% Prepare a property table that contains the list of properties
model = javaObjectEDT(com.jidesoft.grid.PropertyTableModel(propertiesList));
%model.expandAll();
% Prepare the property grid
grid = javaObjectEDT(com.jidesoft.grid.PropertyTable(model));
grid.setShowNonEditable(grid.SHOW_NONEDITABLE_BOTH_NAME_VALUE);
com.jidesoft.grid.TableUtils.autoResizeAllColumns(grid);
%%com.jidesoft.grid.TableUtils.autoResizeAllRows(grid);
grid.setRowHeight(19); % default=16; autoResizeAllRows=20 - we need something in between
grid.putClientProperty('terminateEditOnFocusLost', true); % Auto-end editing upon focus loss
% Prepare the pane containing the grid
pane = javaObjectEDT(com.jidesoft.grid.PropertyPane(grid));
pane.setShowDescription(true);
pane.setShowToolBar(true);
pane.setOrder(2); % uncategorized, unsorted - see http://undocumentedmatlab.com/blog/advanced-jide-property-grids/#comment-42057
% Decide if user wants modal return
if (isModalReturn)
if (~isempty(meta.package.fromName('uiextras')))
[dlg, ~, contentPanel, btnOk, btnCancel] = buildContainerDialog();
else
warning('UiPropEdit:UiExtrasNotFound', 'uiextras package not found ==> old school gui!');
[dlg, contentPanel, btnOk, btnCancel] = buildContainerDialogOldSchool();
end
set(btnOk, 'CallBack', @(varargin) set(dlg, 'UserData', 'Ok'));
set(btnCancel, 'CallBack', @(varargin) set(dlg, 'UserData', 'Cancel'));
hParent = contentPanel;
end
% Create matlab control from java component
%drawnow;
pos = getpixelposition(hParent);
pos(1:2) = 5;
pos = pos - [0,0,10,10];
[~, hPropertyEditorContainer] = javacomponent(pane, pos, hParent);
% Align the background colors
% TODO: Content should adapt to it's parent, shouldn't it ??!!
set(hPropertyEditorContainer, 'Units', 'norm');
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
% Return
if (isModalReturn)
clickedOnOk = false;
set(dlg, 'Visible', 'on');
waitfor(dlg, 'UserData');
if (ishandle(dlg)),
userData = get(dlg, 'UserData');
delete(dlg);
clickedOnOk = ischar(userData) && strcmpi(strtrim(userData), 'ok');
end
% Return of type '[b, updatedData]'
if (nargout >= 1), varargout{1} = clickedOnOk; end
if (nargout >= 2),
if (clickedOnOk), varargout{2} = getUserDataFromJavaObject(buriedDataContainer);
else varargout{2} = []; end
end
setUserDataOnJavaObject(buriedDataContainer, []);
%delete('buriedDataContainer');
else
% Return of type '[hPropertyEditor, jPropertyEditor]'
if (nargout >= 1), varargout{1} = hPropertyEditorContainer; end
if (nargout >= 2), varargout{2} = pane; end
end
%]
end
%% --- Prepare the list of properties for the object
function [propertiesList] = preparePropertiesList(buriedDataContainer, data, callback, path)
%[
% First call (recursive function)
if (nargin < 4), path = ''; end
% Special case when editing a value rather than struct/object
if ((~isstruct(data) && ~isobject(data)) || isempty(data)),
structData.Value = data;
propertiesList = preparePropertiesList(buriedDataContainer, structData, callback, path);
propertiesArray = toArray(propertiesList);
if (length(propertiesArray) == 1)
prop = propertiesArray(1);
prop.setName('_!_NotAnObjectOrStruct_!_');
end
return;
end
% Prepare the list of properties
propertiesList = java.util.ArrayList();
if (numel(data) > 1)
% N-Dimensional structure/object %
% Fix the path because we're not about to inspect
% a sub-field but scalar position in n-d
pathWork = path;
fidx = strfind(pathWork, '.');
if (isempty(fidx)), fidx = 0;
else fidx = fidx(end); end
path = pathWork(1:(fidx-1));
fieldName = pathWork((fidx+1):end);
% For each position
dataSize = size(data);
dataSizeLength = length(dataSize);
positionCount = numel(data);
for idx = 1:positionCount,
% Obtain single element and its position
current = data(idx);
currentPos = cell(1, dataSizeLength);
[currentPos{:}] = ind2sub(dataSize, idx);
currentPos = [currentPos{:}];
posStr = regexprep(num2str(currentPos), ' +', ',');
% Get how to edit the field
editParameters = getDefaultEditionParameters(path, fieldName, current);
editParameters.DisplayName = sprintf('(%s)', posStr);% Fix display name to position
if (~isempty(callback))
editParameters = callback(path, fieldName, current, editParameters);
end
% Case no edit => continue
if (~editParameters.IsVisible), continue; end
% Create a non-editable property to represent current scalar position
prop = javaObjectEDT(com.jidesoft.grid.DefaultProperty);
prop.setName(sprintf('(%s)', posStr));
prop.setValue('');
prop.setEditable(false);
% Recursivly add children fields
children = toArray(preparePropertiesList(buriedDataContainer, editParameters.DisplayValue, callback, sprintf('%s.%s', path, fieldName)));
for childIdx = 1:length(children)
prop.addChild(children(childIdx));
end
% Treat as a simple text field
alignProperty(prop);
finalizePropertyCreation(prop, editParameters, buriedDataContainer);
propertiesList.add(prop);
end
else
% Scalar structure/object %
% Dynamically inspect all the fields and assign corresponding props
if (isobject(data))
isObject = true;
meta = eval(sprintf('?%s', class(data)));
meta = meta.PropertyList;
fieldCount = length(meta);
else
isObject = false;
fieldNames = fieldnames(data);
fieldCount = length(fieldNames);
end
for fieldIdx = 1:fieldCount
% Get field name
if (isObject)
propInfo = meta(fieldIdx);
if (~strcmp(propInfo.GetAccess, 'public'))
continue; %% The property is not public => don't display
end
fieldName = propInfo.Name;
else
fieldName = fieldNames{fieldIdx};
end
% Get field value
try
fieldValue = data.(fieldName);
catch
continue; % The property has no getter
end
% Ask how this field shoud be displayed
editParameters = getDefaultEditionParameters(path, fieldName, fieldValue);
if (isObject && ~strcmp(propInfo.SetAccess, 'public'))
editParameters.IsEditable = false;
end
if (~isempty(callback))
editParameters = callback(path, fieldName, fieldValue, editParameters);
end
% Case field should not be displayed
if (~editParameters.IsVisible), continue; end
% Is the field itself is an object/struct
if (isobject(editParameters.DisplayValue) || isstruct(editParameters.DisplayValue))
% Create a non editable proprety to represent the (object/struct) field
prop = javaObjectEDT(com.jidesoft.grid.DefaultProperty);
prop.setName(fieldName);
prop.setEditable(false);
prop.setValue('');
% Optionally indicate for size if n-dim field
if (numel(editParameters.DisplayValue) > 1)
sz = size(editParameters.DisplayValue);
szStr = regexprep(num2str(sz), ' +', 'x');
prop.setValue(sprintf('[%s]', szStr));
end
% Recursivly add children fields
children = toArray(preparePropertiesList(buriedDataContainer, editParameters.DisplayValue, callback, sprintf('%s.%s', path, fieldName)));
for childIdx = 1:length(children)
prop.addChild(children(childIdx));
end
% Treat as a simple text field
alignProperty(prop);
finalizePropertyCreation(prop, editParameters, buriedDataContainer);
propertiesList.add(prop);
else
% Simple field
prop = newProperty(fieldName, editParameters);
finalizePropertyCreation(prop, editParameters, buriedDataContainer);
propertiesList.add(prop);
end
end
end
end
%% --- Finalize property creation
function [] = finalizePropertyCreation(prop, editParameters, ancillaryData)
%[
% Set additionnal description
prop.setDescription(editParameters.Description);
if (~isempty(editParameters.Description))
renderer = com.jidesoft.grid.CellRendererManager.getRenderer(prop.getType, prop.getEditorContext);
renderer.setToolTipText(editParameters.Description);
end
% Set additionnal category
prop.setCategory(editParameters.Category);
% Set the property's editability state
if (prop.isEditable())
% Set the property's editionParameters.DisplayName to be black
prop.setDisplayName(['<html><font size="4" color="black">' editParameters.DisplayName]);
% Add callbacks for property-change events
hprop = handle(prop, 'CallbackProperties');
set(hprop, 'PropertyChangeCallback', @propUpdatedCallback);
else
% Set the property's editionParameters.DisplayName to be gray
prop.setDisplayName(['<html><font size="4" color="gray">' editParameters.DisplayName]);
end
% Save original object
setUserDataOnJavaObject(prop, ancillaryData);
%]
end
%% --- Default edition parameters for a given field
function [editionParameters] = getDefaultEditionParameters(path, fieldName, fieldValue)
%[
class(path);
editionParameters.IsVisible = true;
editionParameters.IsEditable = true;
editionParameters.Description = '';
editionParameters.Category = '';
editionParameters.DisplayName = strrep(fieldName, '_', ' ');
if (~isempty(editionParameters.DisplayName))
editionParameters.DisplayName(1) = upper(editionParameters.DisplayName(1));
end
if (isempty(fieldValue))
editionParameters.Type = 'string';
editionParameters.DisplayValue = fieldValue;
elseif (isa(fieldValue, 'java.awt.Color'))
editionParameters.Type = 'color';
editionParameters.DisplayValue = fieldValue;
elseif (isa(fieldValue, 'java.awt.Font'))
editionParameters.Type = 'font';
editionParameters.DisplayValue = fieldValue;
elseif (isnumeric(fieldValue))
try % if (length(value) == 3)
colorComponents = num2cell(fieldValue);
if (numel(colorComponents) ~= 3)
error(' '); % bail out if definitely not a color
end
try
editionParameters.DisplayValue = java.awt.Color(colorComponents{:}); % value between 0-1
catch
colorComponents = num2cell(fieldValue/255);
editionParameters.DisplayValue = java.awt.Color(colorComponents{:}); % value between 0-255
end
editionParameters.Type = 'color';
catch % else
if (numel(fieldValue) == 1)
%value = value(1);
if ((fieldValue > now-3650) && (fieldValue < now+3650))
editionParameters.Type = 'date';
editionParameters.DisplayValue = java.util.Date(datestr(fieldValue));
elseif (isa(fieldValue, 'uint') || isa(fieldValue, 'uint8') || isa(fieldValue, 'uint16') || isa(fieldValue, 'uint32') || isa(fieldValue, 'uint64'))
editionParameters.Type = 'unsigned';
editionParameters.DisplayValue = fieldValue;
elseif isinteger(fieldValue)
editionParameters.Type = 'signed';
editionParameters.DisplayValue = fieldValue;
else
editionParameters.Type = 'float';
editionParameters.DisplayValue = fieldValue;
end
else
editionParameters.Type = 'string';
editionParameters.DisplayValue = num2str(fieldValue);
if (size(editionParameters.DisplayValue, 1) > size(editionParameters.DisplayValue, 2))
editionParameters.DisplayValue = editionParameters.DisplayValue';
end
if (size(squeeze(editionParameters.DisplayValue) ,2) > 1)
% Convert multi-row string into a single-row string
editionParameters.DisplayValue = [editionParameters.DisplayValue'; repmat(' ', 1, size(editionParameters.DisplayValue, 1))];
editionParameters.DisplayValue = editionParameters.DisplayValue(:)';
end
editionParameters.DisplayValue = strtrim(regexprep(editionParameters.DisplayValue, ' +', ' '));
if (length(editionParameters.DisplayValue) > 50)
editionParameters.DisplayValue(51:end) = '';
editionParameters.DisplayValue = [editionParameters.DisplayValue '...'];
end
editionParameters.DisplayValue = ['[ ' editionParameters.DisplayValue ' ]'];
end
end
elseif (islogical(fieldValue))
editionParameters.Type = 'boolean';
editionParameters.DisplayValue = fieldValue;
elseif (ischar(fieldValue))
if (exist(fieldValue, 'dir'))
editionParameters.Type = 'folder';
editionParameters.DisplayValue = java.io.File(fieldValue);
elseif (exist(fieldValue, 'file'))
editionParameters.Type = 'file';
editionParameters.DisplayValue = java.io.File(fieldValue);
elseif (fieldValue(1) == '*')
editionParameters.Type = 'password';
editionParameters.DisplayValue = fieldValue;
elseif (sum(fieldValue == '.') == 3)
editionParameters.Type = 'IPAddress';
editionParameters.DisplayValue = fieldValue;
else
editionParameters.Type = 'string';
editionParameters.DisplayValue = fieldValue;
if (length(editionParameters.DisplayValue) > 50)
editionParameters.DisplayValue(51:end) = '';
editionParameters.DisplayValue = [editionParameters.DisplayValue '...'];
end
end
elseif (iscellstr(fieldValue))
editionParameters.Type = fieldValue; % editable if the last cell element is ''
editionParameters.DisplayValue = fieldValue;
elseif (isa(fieldValue, 'java.util.Date'))
editionParameters.Type = 'date';
editionParameters.DisplayValue = fieldValue;
elseif (isa(fieldValue, 'java.io.File'))
if (fieldValue.isFile)
editionParameters.Type = 'file';
editionParameters.DisplayValue = fieldValue;
else % value.isDirectory
editionParameters.Type = 'folder';
editionParameters.DisplayValue = fieldValue;
end
elseif (iscell(fieldValue))
editionParameters.Type = 'string';
editionParameters.DisplayValue = strtrim(regexprep(evalc('disp(fieldValue)'), ' +', ' '));
editionParameters.DisplayValue = ['{ ' editionParameters.DisplayValue ' }'];
elseif (isobject(fieldValue))
editionParameters.Type = 'object';
editionParameters.DisplayValue = fieldValue;
elseif (isstruct(fieldValue))
editionParameters.Type = 'struct';
editionParameters.DisplayValue = fieldValue;
elseif (~isstruct(fieldValue))
editionParameters.Type = 'string';
editionParameters.DisplayValue = strtrim(regexprep(evalc('disp(fieldValue)'), ' +', ' '));
end
%]
end
%% --- Prepare a data property
function prop = newProperty(fieldName, editParameters)
% Create a new property with the chosen editionParameters.DisplayName
prop = javaObjectEDT(com.jidesoft.grid.DefaultProperty);
prop.setName(fieldName);
prop.setDisplayName(editParameters.DisplayName);
prop.setValue(editParameters.DisplayValue);
prop.setEditable(editParameters.IsEditable);
% Set property editor, renderer and alignment
dataType = editParameters.Type;
displayValue = editParameters.DisplayValue;
origDisplayValue = displayValue;
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});
alignProperty(prop, editor);
try prop.setValue(origDisplayValue{1}); catch, end
else
switch lower(dataType)
case 'float', alignProperty(prop, com.jidesoft.grid.CalculatorCellEditor, 'double'); % DoubleCellEditor
case 'boolean', alignProperty(prop, com.jidesoft.grid.BooleanCheckBoxCellEditor, 'logical');
case 'folder', alignProperty(prop, com.jidesoft.grid.FolderCellEditor);
case 'file', alignProperty(prop, com.jidesoft.grid.FileCellEditor);
case 'ipaddress', alignProperty(prop, com.jidesoft.grid.IPAddressCellEditor);
case 'password', alignProperty(prop, com.jidesoft.grid.PasswordCellEditor);
case 'color', alignProperty(prop, com.jidesoft.grid.ColorCellEditor);
case 'font', alignProperty(prop, com.jidesoft.grid.FontCellEditor);
case 'text', alignProperty(prop);
case 'time', alignProperty(prop); % maybe use com.jidesoft.grid.FormattedTextFieldCellEditor ?
case 'signed', %alignProp(prop, com.jidesoft.grid.IntegerCellEditor, 'int32');
model = javax.swing.SpinnerNumberModel(prop.getValue, -intmax, intmax, 1);
editor = com.jidesoft.grid.SpinnerCellEditor(model);
alignProperty(prop, editor, 'int32');
case 'unsigned', %alignProp(prop, com.jidesoft.grid.IntegerCellEditor, 'uint32');
val = max(0, min(prop.getValue, intmax));
model = javax.swing.SpinnerNumberModel(val, 0, intmax, 1);
editor = com.jidesoft.grid.SpinnerCellEditor(model);
alignProperty(prop, editor, 'uint32');
case 'date',
dateModel = com.jidesoft.combobox.DefaultDateModel;
dateFormat = java.text.SimpleDateFormat('dd/MM/yyyy');
dateModel.setDateFormat(dateFormat);
editor = com.jidesoft.grid.DateCellEditor(dateModel, 1);
alignProperty(prop, editor, 'java.util.Date');
try
prop.setValue(dateFormat.parse(prop.getValue)); % convert string => Date
catch
% ignore
end
otherwise, alignProperty(prop); % treat as a simple text field
end
end
end
%% --- Set 'UserData' on a java object
function [] = setUserDataOnJavaObject(h, userData)
oldWarn = warning('off', 'MATLAB:hg:PossibleDeprecatedJavaSetHGProperty');
try
set(h, 'UserData', userData);
warning(oldWarn);
catch
hp = schema.prop(handle(h), 'UserData', 'mxArray'); %#ok<NASGU>
set(handle(h), 'UserData', userData);
warning(oldWarn);
end
end
%% --- Retreive 'UserData' from a java object
function [userData] = getUserDataFromJavaObject(h)
oldWarn = warning('off', 'MATLAB:hg:PossibleDeprecatedJavaSetHGProperty');
try
userData = get(h, 'UserData');
warning(oldWarn);
catch
userData = get(handle(h), 'UserData');
warning(oldWarn);
end
end
%% --- Align a text property to right/left
function alignProperty(prop, editor, propTypeStr, direction)
%[
if ((nargin < 2) || isempty(editor)), editor = com.jidesoft.grid.StringCellEditor; end %(javaclass('char',1));
if ((nargin < 3) || isempty(propTypeStr)), propTypeStr = 'cellstr'; end % => javaclass('char',1)
if ((nargin < 4) || isempty(direction)), 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, end
try editor.getComboBox.setHorizontalAlignment(direction); catch, 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
%% --- Property updated callback function
function propUpdatedCallback(prop, eventData)
%[
% NB: 'eventData.PropertyName' indicates for the type of change on 'prop'
% 'parent' : The parent of 'prop' has changed
% 'name' : The name of 'prop' has changed
% 'value' : The value of 'prop' has changed
if (~strcmp(get(eventData, 'PropertyName'), 'value'));
return; % For now we're only interested in value changes !
end
% 1) Retrieve full data structure
buriedDataContainer = getUserDataFromJavaObject(prop);
data = getUserDataFromJavaObject(buriedDataContainer);
% 2) Get new value plus retrieve full property name
try
oldWarn = warning('off','MATLAB:hg:JavaSetHGProperty');
try prop = java(prop); catch, end
propName = get(prop, 'Name');
propValue = get(prop, 'Value');
while (isa(prop, 'com.jidesoft.grid.Property'))
prop = prop.getParent();
if (prop.isCategoryRow())
continue; % Do not include in the property name: this in fact is a category node !!!
end
newName = char(prop.getName());
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
propName = regexprep(propName, '\.\(', '\('); % For multidimensions replaces all 'lala.(coordinate).fieldname' with 'lala(coordinate).fieldname'
% 3) Convert new values that are still java object
if isjava(propValue)
if isa(propValue,'java.util.Date')
sdf = java.text.SimpleDateFormat('MM-dd-yyyy');
propValue = datenum(sdf.format(propValue).char);
elseif isa(propValue,'java.awt.Color')
propValue = propValue.getColorComponents([])';
else
propValue = char(propValue);
end
end
% 4) Update data structure
if (strcmp(propName, '_!_NotAnObjectOrStruct_!_'))
setUserDataOnJavaObject(buriedDataContainer, propValue);
else
if (propName(1) == '(')
eval(sprintf('data%s = propValue;', propName)); %% Mutlidimensions object
else
eval(sprintf('data.%s = propValue;', propName));
end
setUserDataOnJavaObject(buriedDataContainer, data);
end
%{
% Update the display
checkProps(propsList, hFig);
try propsPane.repaint; catch; end
%}
%]
end
%% --- Returns 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
%% --- Returns the type qualifier for a multidimensional Java array
function [jclassname] = javaarrayclass(mtype, ndims)
%[
switch mtype
case 'logical', jclassid = 'Z'; % logical array of true and false values
case 'char', jclassid = 'C'; % character array
case { 'int8', 'uint8' }, jclassid = 'B'; % 8-bit signed and unsigned integer array
case { 'int16', 'uint16' }, jclassid = 'S'; % 16-bit signed and unsigned integer array
case { 'int32', 'uint32' }, jclassid = 'I'; % 32-bit signed and unsigned integer array
case { 'int64', 'uint64' }, jclassid = 'J'; % 64-bit signed and unsigned integer array
case 'single', jclassid = 'F'; % single-precision floating-point number array
case 'double', jclassid = 'D'; % double-precision floating-point number array
case 'cellstr', jclassid = 'Ljava.lang.String;'; % cell array of strings
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
%% --- Creates dumy data for demonstration purpose
function [data] = createDummyData()
%[
data.multi_dim_struct(2,2).titi = 'hello';
data.multi_dim_struct(2,2).toto = 42;
data.floating_point_property = pi;
data.signed_integer_property = int16(12);
data.unsigned_integer_property = uint16(12);
data.flag_property = true;
data.file_property = mfilename('fullpath');
data.folder_property = pwd;
data.text_property = 'Sample text';
data.fixed_choice_property = {'Yes','No','Maybe'};
data.editable_choice_property = {'Yes','No','Maybe',''}; % editable if the last cell element is ''
data.date_property = java.util.Date; % today's date
data.another_date_property = now-365; % last year
data.time_property = datestr(now,'HH:MM:SS');
data.password_property = '*****';
data.IP_address_property = '10.20.30.40';
data.my_struct.width = 4;
data.my_struct.height = 3;
data.my_struct.and_a_substructure.is_OK = true;
data.numeric_array_property = [11,12,13,14];
data.cell_array_property = {1,magic(3),'text',-4};
data.color_property = [0.4,0.5,0.6];
data.another_color_property = java.awt.Color.red;
data.font_property = java.awt.Font('Arial', java.awt.Font.BOLD, 12);
%try data.class_object_property = matlab.desktop.editor.getActive; catch, end
%]
end
%% --- build container dialog
function [dlg, statusPanel, contentPanel, btnOk, btnCancel] = buildContainerDialog()
%[
% Create the figure
dlg = figure('Name', 'Property editor', ...
'Number', 'off', ...
'Units', 'pixel', ...
'Pos', [300,200,500,500], ...
'Menu', 'none', ...
'Toolbar', 'none', ...
'Visible','off');
% 1) Main grid
mainContainer = uiextras.Grid('Parent', dlg);
% 1.1) Content's panel
contentPanel = uicontainer('Parent', mainContainer);
% 1.2) Container for ok/cancel buttons + statusPanel
okCancelAndStatusContainer = uiextras.Grid('Parent', mainContainer, 'Padding', 5);
% 1.2.1) Status's panel
statusPanel = uicontainer('Parent', okCancelAndStatusContainer);
% 1.2.1) Buttons box
okCancelContainer = uiextras.HButtonBox('Parent', okCancelAndStatusContainer);
btnOk = uicontrol('Parent', okCancelContainer, 'String', 'Ok' );
btnCancel = uicontrol('Parent', okCancelContainer, 'String', 'Cancel');
% 1.2.*) Sizes
set(okCancelAndStatusContainer, 'ColumnSizes', [-1 150]);
% 1.*) Sizes
set(mainContainer, 'RowSizes', [-1 35]);
%]
end
%% --- build container dialog (without uiextras)
function [dlg, contentPanel, btnOk, btnCancel] = buildContainerDialogOldSchool()
%[
% Create the figure
dlg = figure('Name', 'Property editor', ...
'Number', 'off', ...
'Units', 'pixel', ...
'Pos', [300,200,500,500], ...
'Menu', 'none', ...
'Toolbar', 'none', ...
'Visible','off');
margin = 5;
btnHeight = 30;
% Container for the property editor
contentPanel = uicontainer('parent', dlg, 'Units', 'pixel', 'Pos', [0, (margin + btnHeight + margin), 500, 500 - (margin + btnHeight)]);
set(contentPanel, 'Units', 'normalized');
% Create the buttons
btnOk = uicontrol('parent', dlg, 'String', 'OK', 'Units', 'pixel', 'Pos', [50, 5, 60, 30]);
btnCancel = uicontrol('parent', dlg, 'String', 'Cancel', 'Units', 'pixel', 'Pos', [150, 5, 60, 30]);
%]
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment