MATLAB: GUIDE listbox context menu callback fails to update handles using guidata

guidatajava context menulistbox

Hey everybody,
has anyone encountered the following problem when trying to make a GUI using GUIDE. I wanted to have a labeltool, which contains a pushbutton named "add category". When the user press this button, he/she will be prompted to enter a name for a new label category. The GUI also contains a listbox showing/listing all categories the user has ever entered. Now what i want is to have a context menu showing up when right-clicking on an item on the listbox. I achieved this with the help from Undocumented Matlab by modifying the java component underneath the Matlab uicontrol. On the emerging context menu, i have a submenu named "add new category", when selected, it will call the callback of pushbutton "new category". Now the problem is the following:
When i press the pushbutton, every thing works just fine. The pushbutton_Callback(hObject, eventdata, handles) gets called and executes. It also stores the new listitem (using guidata(hObject, handles)) in an handle to feed the listbox, which then shows the new added category. Now when I try to call the pushbutton_Callback from the context menu within the listbox, an error occurs, because the callback input arguments hObject and eventdata are now no longer Matlab UIcontrols, but Java objects. So guidata(hObject, handles) won't work as desired.
I uploaded the corresponding .fig and .m file for the gui. I really appreciate any kind of suggestion and help. The function findjobj.m will be needed for this code to work. This can be downloaded from hier. Just put them all in one folder and the code should run.
This is how the pushbutton callback looks like:
% --- Executes on button press in pushbutton_addCategory.
function pushbutton_addCategory_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton_addCategory (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% prompt user to enter name for new label category
prompt = {'Enter category name:'};
dlg_title = 'Add new label category';
num_lines = 1;
defaultans = {'MyLabelCategory'};
thisLabelCategory = inputdlg(prompt,dlg_title,num_lines,defaultans);
if ~isempty(thisLabelCategory)
% update list box by adding new category
handles.labelCategories{end+1} = char(thisLabelCategory);
set(handles.listbox_labelCategories,'Value',1);
set(handles.listbox_labelCategories,'String',handles.labelCategories);
end
guidata(hObject, handles);
And this is where it gets called from when using the context menu:
% --- Executes on selection change in listbox_labelCategories.
function listbox_labelCategories_Callback(hObject, eventdata, handles)
% Get the listbox's underlying Java control
jScrollPane = findjobj(handles.listbox_labelCategories);
% We got the scrollpane container - get its actual contained listbox control
jListbox = jScrollPane.getViewport.getComponent(0);
% Convert to a callback-able reference handle
jListbox = handle(jListbox, 'CallbackProperties');
% Prepare the context menu
menuItem1 = javax.swing.JMenuItem('Add new category');
% Set the menu items' callbacks
set(menuItem1,'ActionPerformedCallback',{@pushbutton_addCategory_Callback, handles}); % called here!!!!!!
% Add all menu items to the context menu (with internal separator)
jmenu = javax.swing.JPopupMenu;
jmenu.add(menuItem1);
jmenu.addSeparator;
% Set the mouse-click event callback
set(jListbox, 'MousePressedCallback', {@mousePressedCallback,handles.listbox_labelCategories,jmenu});
Thanks a lot in advance.

Best Answer

You shouldn't really be calling a uicomponent's callback manually - it doesn't make sense. Factor out the actual functionality into a function that takes only those arguments it needs (detached from random irrelevant stuff like event data) and call that from both places.
The second point is that you should never pass 'handles' to a callback yourself like this:
set(menuItem1,'ActionPerformedCallback',{@pushbutton_addCategory_Callback, handles});
What this will do is take a copy of the handles struct as it is at the time you make this set call and it will assign that to the callback so you will always get that version of handles in the callback, not the latest. Instead you should pass
handles.figure1
That is the default tag for a GUIDE main figure. I never leave it as that unless I forget, I name it something sensible (in my case I always give it the name of the GUI file). That is a static handle and from it you can call
handles = guidata( hGUI )
where hGUI is the handle you passed in.
So create a new callback for your ActionPerformedCallback, pass in handles.figure1 and have it extract handles and whatever inputs you then need to call the functionality of the pushbutton callback that you earlier factored out into a function that takes sensible inputs.
If you want to be simple then that factored out function can just take the handles struct as an input argument from both places. I don't like this much myself as passing the whole handles structure around all over the place is ugly, but it does at least contain all the things you should need.