Re: problems with editable JList
In article <ldtqtd$8e1$1@dont-email.me>,
markspace <markspace@nospam.nospam> wrote:
On 2/17/2014 12:02 PM, blmblm@myrealbox.com wrote:
markspace <markspace@nospam.nospam> wrote:
I think your event listeners are tripping you up. They're probably
I'd have said not -- based on output of the even-more-debug-prints
version I'm reasonably confident that I'm not getting that kind of
recursion/loop, and anyway I only modify the selection in code to
process add/remove events, rather than in ....
I didn't read your code carefully to find the bug -- there was quite a
lot of it.
Yes. Sorry about that. I was trying for an SSCCE but the "short"
part eluded me.
Rather I just focused on your idea that there might be a bug
in JList or DefaultModelListener. So I purposely started over from
scratch, since that should duplicate the bug if there was one.
Yeah, maybe .... Well, or at least it would show whether what I
had in mind was possible. My thinking is that the library code
shouldn't be setting a selection index outside the bounds of the
underlying model, but maybe it doesn't count as a bug if it only
happens when the user (me) does something wrongheaded.
When it worked, I basically stopped there and posted the code I had. No
sense in belaboring something that's working.
:-)
Anyway, glad you got yourself straightened out. Good luck.
Yes. Thanks again. Like I said, the big difference between what
you did (which works) and what I was doing (which doesn't) is that
you put the code to set the selection in with the code to change
the data model, which -- well, the more I think about it the more
I wonder why I didn't do it that way myself.
For the record-or-whatever, my revised (and working) code is below.
I've folded this change back into my "real" code [*] (which is
itself a prototype for something else), and now it works too. Whew!
[*] Which is not actually Java code but Scala. But there apparently
isn't a Scala Usenet group (only a Google group), and anyway it
seemed like it might be useful to know whether the problem was with
Scala's wrappers around Swing or could be reproduced in straight
Java, which in this case it could.
==== start of code ====
/*
Attempt at an editable JList, supporting allow adding, removing, and
changing elements.
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class TryListMain extends JFrame {
// ---- variables ----
private static boolean DEBUG = true;
private DefaultListModel model;
private TryList list;
private static int counter = 0;
// ---- constructor ----
public TryListMain() {
super();
model = new DefaultListModel();
list = new TryList(model);
setLayout(new BorderLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new ButtonPanel(), BorderLayout.NORTH);
add(list, BorderLayout.CENTER);
}
// ---- methods ----
public TryList getList() { return list; }
public DefaultListModel getModel() { return model; }
public static void debugPrint(String msg) {
if (DEBUG) {
if (msg.length() > 0) {
System.out.println("DBG "+msg);
} else {
System.out.println();
}
}
}
public static void debugPrint() {
debugPrint("");
}
private String generateItem() {
++counter;
return "Element "+counter;
}
// ---- main ----
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
UIManager.put("Button.defaultButtonFollowsFocus", true);
TryListMain theFrame = new TryListMain();
System.out.println("data listeners:");
for (ListDataListener ll :
theFrame.getModel().getListDataListeners())
{
System.out.println(ll.getClass().getName());
}
System.out.println();
System.out.println("list selection listeners:");
for (ListSelectionListener ll :
theFrame.getList().getListSelectionListeners())
{
System.out.println(ll.getClass().getName());
}
System.out.println();
theFrame.setSize(new Dimension(400,200));
theFrame.setVisible(true);
}
});
}
// ---- classes ----
private class ButtonPanel extends JPanel {
private Action removeAction;
private Action replaceAction;
private Action newAction;
private Action showAction;
private void setActionsEnabled() {
int si = list.selectedIndex();
removeAction.setEnabled(si >= 0);
replaceAction.setEnabled(si >= 0);
}
public ButtonPanel() {
super(new FlowLayout());
removeAction = new AbstractAction("Remove") {
@Override
public void actionPerformed(ActionEvent e) {
int si = list.selectedIndex();
TryListMain.debugPrint();
TryListMain.debugPrint("== remove item at "+si);
if (si >= 0) {
model.remove(si);
if (model.getSize() == 0) {
list.clearSelection();
} else {
int newSelect = Math.max(0, si-1);
list.setSelectedIndex(newSelect);
list.ensureIndexIsVisible(newSelect);
}
} else {
// FIXME should never happen
System.err.println("no selected item to remove");
}
}
};
replaceAction = new AbstractAction("Replace") {
@Override
public void actionPerformed(ActionEvent e) {
int si = list.selectedIndex();
if (si >= 0) {
String item = generateItem();
TryListMain.debugPrint();
TryListMain.debugPrint("== replace item at "+si);
model.set(si, item);
} else {
// FIXME should never happen
System.err.println("no selected item to replace");
}
}
};
newAction = new AbstractAction("New") {
@Override
public void actionPerformed(ActionEvent e) {
String item = generateItem();
TryListMain.debugPrint();
TryListMain.debugPrint("== add new item");
model.addElement(item);
int newSelect = model.getSize()-1;
list.setSelectedIndex(newSelect);
list.ensureIndexIsVisible(newSelect);
}
};
showAction = new AbstractAction("Show selected") {
@Override
public void actionPerformed(ActionEvent e) {
int si = list.selectedIndex();
if (si < 0) {
System.out.println("No selected value");
} else {
System.out.println(
String.format("Selected value (%d of %d) '%s'",
si+1, model.getSize(), model.get(si)));
}
}
};
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
setActionsEnabled();
}
});
setActionsEnabled();
add(new JButton(removeAction));
add(new JButton(replaceAction));
add(new JButton(newAction));
add(new JButton(showAction));
}
}
}
class TryList extends JList {
// ---- constructor ----
public TryList(DefaultListModel lModel) {
super(lModel);
getSelectionModel().setSelectionMode(
ListSelectionModel.SINGLE_SELECTION);
addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
TryListMain.debugPrint(String.format(
"selection changed to %s, source %s",
selectedIndicesToString(),
e.getSource().getClass().getName()));
}
});
}
// ---- methods ----
// return -1 if nothing selected, or out of range
public int selectedIndex() {
int[] si = getSelectedIndices();
if ((si.length == 1) && (si[0] < getModel().getSize())) {
return si[0];
} else {
return -1;
}
}
private String selectedIndicesToString() {
StringBuffer sb = new StringBuffer();
for (int i : getSelectedIndices()) {
if (sb.length() > 0) sb.append(' ');
sb.append(i);
}
return "("+sb.toString()+")";
}
}
--
B. L. Massingill
ObDisclaimer: I don't speak for my employers; they return the favor.