2

I have a jComboBox with a few items. The goal is to add an option "All" so when the user select it all the items available would be selected.

Is it possible to do it with a jComboBox or I better use another jComponent (recommendations are welcome). It's required that it's a dropdown list (because it must fit inside a table row) so I guess it could be a menu with checkboxes or something similar.

NK

EDID: I've tried to put a fake object in the model which would be handled specifically (to get all other elements from the model) but it's really not a good idea as this fake object should implement a dozen of interfaces and it's really ugly.

user1228603
  • 81
  • 1
  • 8
  • Try [JList](http://docs.oracle.com/javase/tutorial/uiswing/components/list.html) instead. – dic19 Sep 10 '13 at 14:57
  • It's possible but as your question stands right now, it's hard to give you a good answer. There's many ways to acheive a "select all" feature. Provide either a [SSCCE](http://sscce.org/) or your code. You can do it with a `JComboBox` but without knowing more about your implementation, it might not be the best option. – Jonathan Drapeau Sep 10 '13 at 14:59
  • According to all your comments, the "fake" object is your best option. I'm wondering why an item in a combobox would need a dozen interface to be added to the model... – Jonathan Drapeau Sep 10 '13 at 15:51
  • http://www.coderanch.com/t/343543/GUI/java/MultipleSelect-JComboBox may interest you. – Pshemo Sep 10 '13 at 15:58

3 Answers3

2

It is possible with a JComboBox, however, you need to implement a Renderer and ActionListener for the JComboBox and keep track of the selected items. Example:

private class MultiSelectionHandler<ComboBoxItemType> extends BasicComboBoxRenderer implements ActionListener{

    private static final long serialVersionUID = 1L;
    private ArrayList<Object> selected = new ArrayList<Object>();
    private JComboBox<ComboBoxItemType> comboBox;

    public MultiSelectionHandler(JComboBox<ComboBoxItemType> comboBox){
        this.comboBox = comboBox;
        comboBox.addActionListener(this);
        comboBox.setRenderer(this);
        selected.add(comboBox.getSelectedItem());
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        // Get the now selected item
        ComboBoxItemType selectedItem = comboBox.getItemAt(comboBox.getSelectedIndex());

        if(selectedItem.equals("All")){
            // The the "All"-item has been chosen

            // Select all items (except the "All"-item)
            int itemcount = comboBox.getItemCount();
            for(int i=0; i<itemcount; i++){
                Object item = comboBox.getItemAt(i);
                if (!selected.contains(item) && !item.equals("All") ){
                    selected.add(item);
                }
            }
        } else {
            // Another option has been chosen
            // Toggle the selection
            if(selected.contains(selectedItem)) {  
                selected.remove(selectedItem);  
            } else if(!selected.contains(selectedItem)) {  
                selected.add(selectedItem);  
            }  
        }
    }

    @Override
    public Component getListCellRendererComponent(JList list,  
            Object value,  
            int index,  
            boolean isSelected,  
            boolean cellHasFocus) {  

        // Set the background and foreground colors, use the selection colors if the item
        // is in the selected list
        if (selected.contains(value)) {  
            setBackground(list.getSelectionBackground());  
            setForeground(list.getSelectionForeground());  
        } else {  
            setBackground(list.getBackground());  
            setForeground(list.getForeground());  
        }  

        // Set the text of the item
        setText((value == null) ? "" : value.toString());  
        return this;
    }
}

(it seems to work at first glance, but probably is littered with bugs ;-) )

Then you can use this multi-selection-thingy like this:

// Create the items
String[] arr = {"One", "Two", "Three", "All"};

// Create the model and the box
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<String>(arr);  
JComboBox<String> box = new JComboBox<String>(model);

// Use the MultiSelectionHandler for rendering
new MultiSelectionHandler<String>(box);
t_over
  • 122
  • 4
  • Good solution but there is a drawback. If we put the array "arr" we actually put a fake object (the string "All" ) in the model and then process it specially upon selection (I prefer the component to do this for me so I can reuse it and not to do post processing every time). Putting a fake object inside the model is one of the things I want to avoid. – user1228603 Sep 11 '13 at 08:17
  • Actually I can do this "post processing" inside the component and reuse it nicely. But I still need to put a fake object in the model (unless I make a copy of the model but this may cause synchronization problems). – user1228603 Sep 11 '13 at 08:51
2

Here's a table cell editor that pops up a dialog that contains a JList of checkboxes. It's a mashup of a variety of examples from here and elsewhere.

EDIT: added the Select All feature

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;

import javax.swing.*;
import javax.swing.table.*;

/*
 * A table cell editor that pops up a JList of checkboxes.
 */
@SuppressWarnings("unchecked")
public class CheckListEditor extends DefaultCellEditor implements Runnable
{
  private PopupDialog popup;
  private JButton editorComp;
  private String currentText = "";

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

  public void run()
  {
    String[] columnNames = { "Item", "Selections" };
    Object[][] data = { { "Item 1", "A" },
                        { "Item 2", "B" },
                        { "Item 3", "C" } };

    JTable table = new JTable(data, columnNames);
    table.getColumnModel().getColumn(1).setPreferredWidth(300);
    table.setPreferredScrollableViewportSize(table.getPreferredSize());

    CheckListEditor popupEditor = new CheckListEditor();
    popupEditor.setList(Arrays.asList(new String[]{"A", "B", "C"}));
    table.getColumnModel().getColumn(1).setCellEditor(popupEditor);

    JScrollPane scrollPane = new JScrollPane(table);
    scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

    JFrame frame = new JFrame("Multi-Select Editor");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().setLayout(new FlowLayout());
    frame.getContentPane().add(scrollPane);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public CheckListEditor()
  {
    super(new JTextField());
    setClickCountToStart(2);

    // Use a JButton as the editor component
    editorComp = new JButton();
    editorComp.setFont(editorComp.getFont().deriveFont(Font.PLAIN));
    editorComp.setBackground(Color.WHITE);
    editorComp.setBorderPainted(false);
    editorComp.setContentAreaFilled(false);
    editorComp.setHorizontalAlignment(SwingConstants.LEFT);

    // Set up the dialog where we do the actual editing
    popup = new PopupDialog();
  }

  @Override
  public Object getCellEditorValue()
  {
    return currentText;
  }

  @Override
  public Component getTableCellEditorComponent(JTable table, Object value,
      boolean isSelected, int row, int column)
  {
    SwingUtilities.invokeLater(new Runnable()
    {
      public void run()
      {
        popup.setValue(currentText);
        Point p = editorComp.getLocationOnScreen();
        popup.setLocation(p.x, p.y + editorComp.getSize().height);
        popup.setVisible(true);
        fireEditingStopped();
      }
    });

    currentText = value.toString();
    editorComp.setText(currentText);
    return editorComp;
  }

  public <T> void setList(List<T> items)
  {
    popup.setList(items);
  }

  /*
   * Dialog that contains the "editor" panel
   */
  class PopupDialog extends JDialog
  {
    private final String delimiter = ",";
    private JList jlist;

    public PopupDialog()
    {
      super((Frame) null, "Select", true);

      setUndecorated(true);
      getRootPane().setWindowDecorationStyle(JRootPane.NONE);

      jlist = new JList();
      jlist.setCellRenderer(new CheckBoxListRenderer());
      jlist.setPrototypeCellValue(new CheckListItem("12345678901234567890"));
      jlist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      jlist.setVisibleRowCount(5);
      jlist.addMouseListener(new MouseAdapter()
      {
        @Override
        public void mouseClicked(MouseEvent event)
        {
          selectItem(event.getPoint());
        }
      });

      JScrollPane scrollPane = new JScrollPane(jlist);
      scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

      JButton selectAll = new JButton("Select All");
      selectAll.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent event)
        {
          selectAllItems();
        }
      });

      JButton ok = new JButton("OK");
      ok.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent event)
        {
          closeDialog(true);
        }
      });

      JButton cancel = new JButton("Cancel");
      cancel.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent event)
        {
          closeDialog(false);
        }
      });

      JPanel buttons = new JPanel();
      buttons.add(selectAll);
      buttons.add(ok);
      buttons.add(cancel);

      JPanel panel = new JPanel(new BorderLayout());
      panel.setBorder(BorderFactory.createLineBorder(Color.BLUE, 1));
      panel.add(scrollPane, BorderLayout.CENTER);
      panel.add(buttons, BorderLayout.SOUTH);
      setContentPane(panel);
      pack();

      getRootPane().setDefaultButton(ok);
    }

    private void selectAllItems()
    {
      ListModel model = jlist.getModel();
      for (int index = 0; index < model.getSize(); index++)
      {
        CheckListItem item = (CheckListItem) model.getElementAt(index);
        item.setSelected(true);
      }
      jlist.repaint();
    }

    /*
     * Save the changed text before hiding the popup
     */
    public void closeDialog(boolean commit)
    {
      if (commit)
      {
        currentText = getSelectedItems();
      }

      jlist.requestFocusInWindow();
      setVisible(false);
    }

    private void selectItem(Point point)
    {
      int index = jlist.locationToIndex(point);

      if (index >= 0)
      {
        CheckListItem item = (CheckListItem) jlist.getModel().getElementAt(index);
        item.setSelected(!item.isSelected());
        jlist.repaint(jlist.getCellBounds(index, index));
      }
    }

    private <T> void setList(List<T> items)
    {
      Vector<CheckListItem> listData = new Vector<CheckListItem>();
      for (T item : items)
      {
        listData.add(new CheckListItem(item));
      }
      jlist.setListData(listData);
    }

    public String getSelectedItems()
    {
      String text = "";
      ListModel model = jlist.getModel();

      for (int i = 0; i < model.getSize(); i++)
      {
        CheckListItem item = (CheckListItem) model.getElementAt(i);
        if (item.isSelected())
        {
          text += item.toString();
          text += delimiter;
        }
      }

      if (text.endsWith(delimiter))
      {
        text = text.substring(0, text.lastIndexOf(delimiter));
      }

      return text;
    }

    public void setValue(String value)
    {
      ListModel model = jlist.getModel();

      for (int i = 0; i < model.getSize(); i++)
      {
        ((CheckListItem) model.getElementAt(i)).setSelected(false);
      }

      String text = value == null ? "" : value.toString();
      String[] tokens = text.split(delimiter);

      for (String token : tokens)
      {
        for (int i = 0; i < model.getSize(); i++)
        {
          if (model.getElementAt(i).toString().equals(token))
          {
            ((CheckListItem) model.getElementAt(i)).setSelected(true);
          }
        }
      }

      jlist.clearSelection();
      jlist.ensureIndexIsVisible(0);
    }
  }

  private class CheckBoxListRenderer extends JCheckBox implements ListCellRenderer
  {
    public Component getListCellRendererComponent(JList comp, Object value, int index,
                                                  boolean isSelected, boolean hasFocus)
    {
      setEnabled(comp.isEnabled());
      setSelected(((CheckListItem) value).isSelected());
      setFont(comp.getFont());
      setText(value.toString());

      if (isSelected)
      {
        setBackground(comp.getSelectionBackground());
        setForeground(comp.getSelectionForeground());
      }
      else
      {
        setBackground(comp.getBackground());
        setForeground(comp.getForeground());
      }

      return this;
    }
  }

  private class CheckListItem
  {
    private Object item;
    private boolean selected;

    public CheckListItem(Object item)
    {
      this.item = item;
    }

    public boolean isSelected()
    {
      return selected;
    }

    public void setSelected(boolean isSelected)
    {
      this.selected = isSelected;
    }

    @Override
    public String toString()
    {
      return item.toString();
    }
  }
}
splungebob
  • 5,357
  • 2
  • 22
  • 45
1

You could try with JList instead (see How to Use Lists) using SINGLE_SELECTION_INTERVAL or MULTIPLE_SELECTION_INTERVAL mode to achieve your target.

 JList list = new JList(new DefaultListModel());
 list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

 JButton selectAll = new JButton("Select All");
 selectAll.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
          DefaultListModel dlm = (DefaultListModel)list.getModel();                
          list.setSelectionInterval(0, dlm.getSize() - 1);
      }
  });
dic19
  • 17,821
  • 6
  • 40
  • 69
  • jList is a fair option It's expandable and we can select all the items. The problem is that it's not exactly what we need. For example if we have lots of items user will need to do lots of clicks instead of making a single "All" click. @Jonathan Drapeau unfortunately I can not provide you with a SSCCE because we have internal framework and interfaces and etc. – user1228603 Sep 10 '13 at 15:27
  • Hi there. That's exactly the point. In my code I already made a `selectAll` button which selects all components in the list ;) – dic19 Sep 10 '13 at 15:30
  • Currently the jComboBox is inside a table (two of the columns are jComboBox-es) so I have no space to add additional button(s) for the "SelectAll". – user1228603 Sep 10 '13 at 15:33