Setting line position in an edit-box uicontrol

I often see requests in the Matlab forum (CSSM) regarding modifying some uicontrol property that is not exposed by Matlab. Since all Matlab uicontrols are based on underlying Java Swing controls, accessing these features is possible via their Java control peers.

In this post I will give the example of setting the caret (cursor) position in a multi-line edit-box. Apparently, whenever the string contents of such a uicontrol is modified, Matlab automatically sets the caret position on the first character of the first line. It is often requested to place the caret on the last line, so that the last line scrolls into view, rather than the first line. This is the case, for example, if you wish to display an event log that keeps adding new entries at the bottom.

The first step in accessing the underlying Java control is to download my FindJObj submission on the File Exchange. FindJObj searches down the window frame hierarchy until it finds a Java control with the exact position and size of the requested Matlab HG handle (FindJObj has lots of other goodies which will be described in another post).

Once we have the Java peer reference handle (in our case, a UIScrollPane object), we select its internal edit-box control (an object of class com.mathworks.hg.peer.EditTextPeer$hgTextEditMultiline) and use its setCaretPosition() method to move the caret to the end of the text:

>> % Create the multi-line edit-box
>> str = {'multi','line','editbox'};
>> hEdit = uicontrol('style','edit','max',3,'string',str);

Editbox caret at top row (default)

Editbox caret at top row (default)

>> % Get the underlying Java control peer (a scroll-pane object)
>> jhEdit = findjobj(hEdit)
jhEdit =
	javahandle_withcallbacks.com.mathworks.hg.peer.utils.UIScrollPane

>> % Check that the scrollpane has a multiline edit control and 2 scrollbars
>> jhEdit.list
com.mathworks.hg.peer.utils.UIScrollPane[...]
 javax.swing.JViewport[...]
  com.mathworks.hg.peer.EditTextPeer$hgTextEditMultiline[...]
 com.mathworks.hg.peer.utils.UIScrollPane$1[...]
  com.sun.java.swing.plaf.windows.WindowsScrollBarUI$WindowsArrowButton[...]
  com.sun.java.swing.plaf.windows.WindowsScrollBarUI$WindowsArrowButton[...]
 com.mathworks.hg.peer.utils.UIScrollPane$2[...]
  com.sun.java.swing.plaf.windows.WindowsScrollBarUI$WindowsArrowButton[...]
  com.sun.java.swing.plaf.windows.WindowsScrollBarUI$WindowsArrowButton[...]

>> % Get the scroll-pane's internal edit control
>> jEdit = jhEdit.getComponent(0).getComponent(0)
jEdit =
com.mathworks.hg.peer.EditTextPeer$hgTextEditMultiline[...]

>> % Now move the caret position to the end of the text
>> jEdit.setCaretPosition(jEdit.getDocument.getLength);

Editbox caret at bottom (via Java)

Editbox caret at bottom (via Java)

Setting the caret position works, but is actually better done on the EDT using awtinvoke or javaMethodEDT. This will be explained in a separate EDT-specific article.

Categories: GUI, Java, Medium risk of breaking in future versions, UI controls

Tags: , , ,

Bookmark and SharePrint Print

21 Responses to Setting line position in an edit-box uicontrol

  1. Heiko says:

    Hi. Yesterday I stepped over the findjobj function (actually a very fine piece of work, thank you very much!) while looking for a way to link the selected cells of multiple uitable objects (R2009a). I have four uitables showing related data at different processing stages. What I wanted was to click on one table and select the same cell in all four tables, which is not a native uitable property to set (or even get! The selected cell is given only in the CellSelectionEvent). With findjobj I found several promising methods. The methods “setRowSelectionInterval” and “setColumnSelectionInterval” seemed to link to the method “setSelectionInterval” of the javax.swing.DefaultListSelectionModel but it didn’t work at all. The method “changeSelection” (seems to be a wrapper from the MathWorks) finally did almost what I wanted. It selected the correct cells when I first clicked on a table, but after that, clicking on the tables directly entered the cell editing mode, even if this was disabled for the uitable object, and so the cellSelectionEvent was not fired anymore. I also couldn’t find out what the parameters of the method are. It takes four of them, the first is the row, the second is the column. The other two do change the behavior in an undescribable way. Do you have any hints where I can find out about the parameters of “changeSelection”, and if there is something else I could do to get the behavior I like to have? Maybe I misunderstood the SelectionInterval-functionality, but it didn’t change anything on the java objects. Thank you for reading!

    • Yair Altman says:

      You can do 2 things with a standard JTable and the old uitable, but I’m not sure they work with the new uitable (no harm trying):

      1. Set all the table columns as non-editable using somthing like the following:

      for colIdx = 0 : numCols
      tablePeer.setEditable(colIdx,0); % 1:end doesn’t work…
      end

      2. Set all the table column CellEditors to have infinite ClickCountToStart, like this:

      for colIdx = 0 : numCols
      try
      editor=tablePeer.getColumnModel.getColumn(colIdx).getCellEditor;
      editor.setClickCountToStart(intmax); % i.e, never enter edit mode…
      catch
      % never mind…
      end
      end

      One or both of these may work for you.

    • Heiko says:

      Thank you for the answer.

      Unfortunately, both of your options don’t work. But while trying I found that fiddling with the java settings alters the uitable properties in an unpredictable way. For instance, the ColumnEditable property is reset to default (1), which caused the described behavior.

      Another problem is, that there seems to be a loop through many different callbacks that call my CellSelectionCallback. Especially from the second selection it takes seconds for the tables to react, and the result is wrong.

      I stop working on this for now. Thank you again! Bye

  2. A. Bindemann says:

    Yair,

    I’m having a problem with findjobj. It returns an empty result when I pass it the MATLAB handle of an edit box. The problem appears to begin in the call to getRootPanel on line 319 (jRootPane = jFigPanel.getRootPane).

    The function falls through to line 346 (jRootPane = jRootPane.getTopLevelAncestor;) which also returns an empty result.

    I’m trying to use a MATLAB GUI to display a message log, and would like to position the cursor at the end of the edit box. Interestingly, the edit box behavior under R14SP3 always placed the cursor at the end of the edit box. The behavior changed somewhere between that version and R2007b.

    Any ideas you might have would be appreciated.

    Thanks,
    A. Bindemann

    • Yair Altman says:

      @Bindermann – I can think of two reasons for this problem:

      1. the figure or one of its components is hidden, thereby causing a problem in retrieving the RootPane and/or TopLevelAncestor (=Frame peer) references.

      2. are you using the latest version of FindJObj? – one of the latest versions increased the size/position tolerance used when trying to locate a handle onscreen and this may solve the problem of FindJObj not finding this handle.

      If none of these appear to be the cause, then please email me a reproducible code snippet and I’ll try to fix the problem.

  3. Sylvain says:

    Hi everyone,

    I have used the FindObj fonction (great job M. Altman) and the carret does go to the end of the editbox, but the problem is that the view don’t follow the carret…
    I would like to know if you have any idea, maybe a simple setting that I don’t know , to get the scrollbar at the bottom of the text (I am quite a beginner with the whole matlab graphic interface).

    Regards,

    Sylvain

  4. Johannes says:

    Hello Yair,

    first of all, thank you for the superb coding, findjobj has saved me from a lot of trouble. I am using your code to set the caret position to the last line inside an edit text box (used as a log inside my GUI) whenever I update the output.
    One problem remains: To write something inside the edit text box, I am using

    % get existing text
    exTex=get(handles.outputWindow,'String');
    % new Text to be appended
    addTest=sprintf('Some new text');
    % write new text 
    newText=sprintf('%s \r %s ', exText, addText);
    % update output handle
    set(handles.outputWindow,'String',newText);

    The problem now is: the set command in the last line resets the scroll bar to the top. So if some lines stream into the output window, it jumps to the top and quickly back to the bottom (due to the set.caretPosition). Is there any other way I could write lines into my output window, without resetting the scroll bar to the top?

    Thanks and lots of regards from Germany!

    • Johannes says:

      Never mind, I found a different solution using listboxes. It works now as I want it to be.

      All the best,
      Johannes

    • Nick says:

      Johannes,

      Similar to you, I’m adding a console/log window to a GUI and would like the scroll bar at the bottom. With a list box, were you able to get rid of the ‘jump’ problem altogether?

      I think there might be a solution using an edit box by diving deeper into the underlying Java. However, I have only recently started using the findjobj utility, so I could be wrong about that.

      Nick

  5. Sainath M says:

    Hello Yair,

    I am facing one problem when I write Java methods in Matlab.
    Ex:
    >> % Create the multi-line edit-box
    >> str = {‘multi’,’line’,’editbox’};
    >> hEdit = uicontrol(‘style’,’edit’,’max’,3,’string’,str);

    This is the script I am writing in Matlab command window. After that
    when I type the code

    >> jhEdit = findjobj(hEdit)

    Matlab is showing error

    ??? Undefined function or method ‘findjobj’ for input arguments of type
    ‘double’.

    It is the first time I am using Jmethods in Matlab, will you please guide me what is the problem…

    Thank you for your time,

    Regards,
    Sainath M.

  6. RK MacLean says:

    Using another one of Yair’s suggestions worked better for me:

    jhEdit = findjobj(handles.status_text);
    jhEdit.anchorToBottom;
  7. Thomas says:

    Hi Yair,

    I have a multi-line editbox with a scrollbar (vertical as needed) and I want it, when activted, to be always on the botton.
    I tryed this

    jVScroll = jScrollPane.getVerticalScrollBar;
    jVScroll.setValue(jVScroll.getMaximum);
    jScrollPane.repaint;

    and this

    jhEdit = findjobj(handles.my_editbox);
    jhEdit.anchorToBottom;

    but didn’t work… Can you tell me what am I doing wrong?
    Thanks

    • @Thomas, perhaps this is done when the control is still not visible, perhaps you’re modifying the control in Matlab later on (this resets the underlying component), perhaps something else. You’ll need to debug it step-by-step to see where the problem is.

  8. Tiago Silva says:

    Hello Yair,

    I’m trying to use something similar to this in order to remember the position of a uitable scrollbar after a data update. My problem is that although I got it work, it only really works in debug mode. The gui I’m developing is fairly complicated, but even a simple example code like the following only works as I’d expect it to do in debug mode:

    h = uitable;
    set(h,'Data',rand(20));
    pause(1);
    jh = findjobj(h);
    jhScroll = jh.getVerticalScrollBar;
    position = 100;
    set(h,'Data',rand(20));
    jhScroll.setValue(position);

    When running this with breakpoints and stepping each line at a time I end up with the uitable updated and the scrollbar at the bottom, as I expected it to behave. Executing the code normally, I can see the uitable is updated but I end up with the scrollbar at the top.

    Any idea of what I might be doing wrong? Thanks in advance.

  9. Ana GG says:

    Hello,
    This seems to work fine for editbox, but how can this be extended to a cell of uitable?
    My specific problem is: I have a hint text on a specific editable (text) cell of a table. When I click this cell I want the text to disappear but still I want the cursor to be in the cell to introduce the new value. I have done the first part, but to be able to write on this cell I need to click again, and it would be good if I didn’t have to do it.
    Thank you very much in advance,
    Ana

  10. Scott says:

    I’m now using R2017a, and code that used to work with anchorToBottom is no longer working… any ideas?

    >> str = {'multi','line','editbox'};
    >> hEdit = uicontrol('style','edit','max',3,'string',str);
    >> jhEdit = findjobj(hEdit)
    jhEdit =
        1×0 empty handle

Leave a Reply

Your email address will not be published. Required fields are marked *