-
-
Save CitizenInsane/3ce1755bf0901f0bae38 to your computer and use it in GitHub Desktop.
Graphical edition of fields (or properties) of a matlab structure (or object).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
%] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
% | |
% 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