Question

After almost a whole afternoon of attempts and search on the web/SO, I'm asking advices, and my question is:

How to programmatically freeze the current selection in a JList and revert back to normal behaviour?

First of all a bit of context: I've got a JList with a list of item, on each can be fired a computation that could take long time (60 secs), that is performed asynchronously by a SwingWorker. A progress bar shows that the program is actually computing and it is not freezed. While the thread is working, I don't want the user to be able to change the selected element in my list. When the thread finishes I would like to revert back to the normal behaviour of the list.

I list out my attempts:

1) testing my "condition" (isComputing() method) in the ListSelectionListener and simply returning if it is computing:

@Override
public void valueChanged(ListSelectionEvent e) {
    if(e.getSource().equals(listSCCs)){
        if(isComputing())
            return;
[...]
}

The problem is that I realized the listener is fired after any selection change, so it is not helping.

2) Add a completely empty MouseListener implementation on the list when computing, removing it at the end of the computation. In my idea it would have catched the mouse events of the list and voided them, but the list seems unaffected by this.

3) My last chance is to change back to the "original" selection when computing status is active in the ListSelectionListener (if the new selection is different from the "original" one, to avoid infinite recursion on the change event), but I really don't like this solution and I hope there are more elegant ones.

Was it helpful?

Solution

Just disable the JList while computing. Here's a demo where you can mark a JCheckBox to simulate when your computation starts, and uncheck when done. The selection is maintained.

package swing;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class JListDisabledListDemo implements Runnable
{
  private static final String ITEMS[] =
    { "Black", "Blue", "Green", "Orange", "Purple", "Red", "White"};

  private JList jList;
  private JCheckBox chkEnable;

  public static void main(String args[])
  {
    SwingUtilities.invokeLater(new JListDisabledListDemo()); 
  }

  public void run()
  {
    jList = new JList(ITEMS);
    jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    JScrollPane scroll = new JScrollPane(jList);
    scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

    chkEnable = new JCheckBox("Enable", true);
    chkEnable.addItemListener(new ItemListener()
    {
      public void itemStateChanged(ItemEvent e)
      {
        jList.setEnabled(chkEnable.isSelected());
      }
    });

    JFrame f = new JFrame("Colors");
    Container contentPane = f.getContentPane();
    contentPane.setLayout(new BorderLayout());
    contentPane.add(scroll, BorderLayout.CENTER);
    contentPane.add(chkEnable, BorderLayout.NORTH);

    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setSize(180, 220);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}

OTHER TIPS

You can disable the JList before execute the SwingWorker (of course do it in the Event Dispatch Thread) and enable the list overriding done() method:

protected void done()

Executed on the Event Dispatch Thread after the doInBackground method is finished. The default implementation does nothing. Subclasses may override this method to perform completion actions on the Event Dispatch Thread. Note that you can query status inside the implementation of this method to determine the result of this task or whether this task has been cancelled.

Assuming you have a button that fires the computation then your code will look like this:

final JProgressBar progressBar = new JProgressBar();
final JList list = new JList();
final JButton button = new JButton("Process...");

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        if(list.getSelectedValue() != null) {
            list.setEnabled(false);
            button.setEnabled(false);
            progressBar.setValue(0);

            SwingWorker<Void, Integer> worker = new SwingWorker<Void, Integer>() {
                @Override
                protected Void doInBackground() throws Exception {
                    // your code here
                    return null;                       
                }

                @Override
                protected void process(List<Integer> chunks) {
                    progressBar.setValue(chunks.get(chunks.size() - 1));
                }

                @Override
                protected void done() {
                    list.setEnabled(true);
                    button.setEnabled(true);
                }
            };                    
            worker.execute();
        }
    }
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top