All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jidesoft.swing.CheckBoxListWithSelectable Maven / Gradle / Ivy

There is a newer version: 3.6.18
Show newest version
/*
 * @(#)CheckBoxList.java 4/21/2005
 *
 * Copyright 2002 - 2005 JIDE Software Inc. All rights reserved.
 */
package com.jidesoft.swing;

import javax.swing.*;
import javax.swing.text.Position;
import java.awt.*;
import java.awt.event.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

/**
 * CheckBoxListWithSelectable is a special JList which uses JCheckBox as the list element. In addition to
 * regular JList feature, it also allows you select any number of elements in the list by selecting the check boxes.
 * 

* The element is ListModel should be an instance of {@link Selectable}. If you have your own class that represents the * element in the list, you can implement Selectable and implements a few very simple methods. If your * elements are already in an array or Vector that you pass in to the constructor of JList, we will convert them to * {@link DefaultSelectable} which implements Selectable interface. *

* To select an element, user can mouse click on the check box, or highlight the rows and press SPACE key to toggle the * selections. *

* To listen to the check box selection change, you can call addItemListener to add an ItemListener. *

* Please note, there are two implementations of CheckBoxList. CheckBoxListWithSelectable is one. There is also another * one call CheckBoxList. CheckBoxListWithSelectable is actually the old implementation. In 1.9.2, we introduced a new * implementation and renamed the old implementation to CheckBoxListWithSelectable. The main difference between the two * implementation is at how the selection state is kept. In new implementation, the selection state is kept at a * separate ListSelectionModel which you can get using {@link CheckBoxList#getCheckBoxListSelectionModel()}. The old * implementation kept the selection state at Selectable object in the ListModel. */ public class CheckBoxListWithSelectable extends JList implements ItemSelectable { protected CheckBoxListCellRenderer _listCellRenderer; public static final String PROPERTY_CHECKBOX_ENABLED = "checkBoxEnabled"; public static final String PROPERTY_CLICK_IN_CHECKBOX_ONLY = "clickInCheckBoxOnly"; private boolean _checkBoxEnabled = true; private boolean _clickInCheckBoxOnly = true; /** * Constructs a CheckBoxList with an empty model. */ public CheckBoxListWithSelectable() { init(); } /** * Constructs a CheckBoxList that displays the elements in the specified Vector. If the * Vector contains elements which is not an instance of {@link Selectable}, it will wrap it automatically into * {@link DefaultSelectable} and add to ListModel. * * @param listData the Vector to be loaded into the data model */ public CheckBoxListWithSelectable(final Vector listData) { super(wrap(listData)); init(); } /** * Constructs a CheckBoxList that displays the elements in the specified Object[]. If the * Object array contains elements which is not an instance of {@link Selectable}, it will wrap it automatically into * {@link DefaultSelectable} and add to ListModel. * * @param listData the array of Objects to be loaded into the data model */ public CheckBoxListWithSelectable(final Object[] listData) { super(wrap(listData)); init(); } /** * Constructs a CheckBoxList that displays the elements in the specified, non-null model. * All CheckBoxList constructors delegate to this one. *

* Please note, if you are using this constructor, please make sure all elements in dataModel are instance of {@link * Selectable}. * * @param dataModel the data model for this list * @throws IllegalArgumentException if dataModel is null */ public CheckBoxListWithSelectable(ListModel dataModel) { super(wrap(dataModel)); init(); } /** * Initialize the CheckBoxList. */ protected void init() { setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); _listCellRenderer = createCellRenderer(); Handler handle = createHandler(); addMouseListener(handle); addKeyListener(handle); } /** * Creates the cell renderer. * * @return the cell renderer. */ protected CheckBoxListCellRenderer createCellRenderer() { return new CheckBoxListCellRenderer(); } /** * Creates the mouse listener and key listener used by CheckBoxList. * * @return the Handler. */ protected Handler createHandler() { return new Handler(this); } /** * Sets the selected elements. * * @param elements the elements to be selected */ public void setSelectedObjects(Object[] elements) { Map selected = new HashMap(); for (Object element : elements) { selected.put(element, ""); } setSelectedObjects(selected); } /** * Sets the selected objects. * * @param objects the elements to be selected in a Vector. */ public void setSelectedObjects(Vector objects) { Map selected = new HashMap(); for (Object element : objects) { selected.put(element, ""); } setSelectedObjects(selected); } @Override public ListCellRenderer getCellRenderer() { if (_listCellRenderer != null) { _listCellRenderer.setActualListRenderer(super.getCellRenderer()); return _listCellRenderer; } else { return super.getCellRenderer(); } } public ListCellRenderer getActualCellRenderer() { if (_listCellRenderer != null) { return _listCellRenderer.getActualListRenderer(); } else { return super.getCellRenderer(); } } private void setSelectedObjects(Map selected) { for (int i = 0; i < getModel().getSize(); i++) { Object elementAt = getModel().getElementAt(i); if (elementAt instanceof Selectable) { Selectable selectable = (Selectable) elementAt; if (selectable instanceof DefaultSelectable) { elementAt = ((DefaultSelectable) selectable).getObject(); } if (selected.get(elementAt) != null) { selectable.setSelected(true); fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, selectable, ItemEvent.SELECTED)); selected.remove(elementAt); if (selected.size() == 0) { break; } } else { if (selectable.isSelected()) { selectable.setSelected(false); fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, selectable, ItemEvent.DESELECTED)); } } } } repaint(); } private static ListModel wrap(ListModel dataModel) { for (int i = 0; i < dataModel.getSize(); i++) { if (!(dataModel.getElementAt(i) instanceof Selectable)) { throw new IllegalArgumentException("The ListModel contains an element which is not an instance of Selectable at index " + i + "."); } } return dataModel; } private static Selectable[] wrap(Object[] objects) { if (objects instanceof Selectable[]) { return (Selectable[]) objects; } else { Selectable[] elements = new Selectable[objects.length]; for (int i = 0; i < elements.length; i++) { elements[i] = new DefaultSelectable(objects[i]); } return elements; } } private static Vector wrap(Vector objects) { Vector elements = new Vector(); for (Object o : objects) { if (o instanceof Selectable) { elements.add((Selectable) o); } else { elements.add(new DefaultSelectable(o)); } } return elements; } /** * Gets the value of property clickInCheckBoxOnly. If true, user can click on check boxes on each tree node to * select and deselect. If false, user can't click but you as developer can programmatically call API to * select/deselect it. * * @return the value of property clickInCheckBoxOnly. */ public boolean isClickInCheckBoxOnly() { return _clickInCheckBoxOnly; } /** * Sets the value of property clickInCheckBoxOnly. * * @param clickInCheckBoxOnly true to allow to check the check box. False to disable it which means user can see * whether a row is checked or not but they cannot change it. */ public void setClickInCheckBoxOnly(boolean clickInCheckBoxOnly) { if (clickInCheckBoxOnly != _clickInCheckBoxOnly) { boolean old = _clickInCheckBoxOnly; _clickInCheckBoxOnly = clickInCheckBoxOnly; firePropertyChange(PROPERTY_CLICK_IN_CHECKBOX_ONLY, old, _clickInCheckBoxOnly); } } protected static class Handler implements MouseListener, KeyListener { protected CheckBoxListWithSelectable _list; int _hotspot = new JCheckBox().getPreferredSize().width; public Handler(CheckBoxListWithSelectable list) { _list = list; } protected boolean clicksInCheckBox(MouseEvent e) { int index = _list.locationToIndex(e.getPoint()); Rectangle bounds = _list.getCellBounds(index, index); if (bounds != null) { if (_list.getComponentOrientation().isLeftToRight()) { return e.getX() < bounds.x + _hotspot; } else { return e.getX() > bounds.x + bounds.width - _hotspot; } } else { return false; } } public void mouseClicked(MouseEvent e) { } public void mousePressed(MouseEvent e) { if (!_list.isCheckBoxEnabled() || !_list.isEnabled()) { return; } if (!_list.isClickInCheckBoxOnly() || clicksInCheckBox(e)) { int index = _list.locationToIndex(e.getPoint()); toggleSelection(index); } } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void keyPressed(KeyEvent e) { if (!_list.isCheckBoxEnabled() || !_list.isEnabled()) { return; } if (e.getModifiers() == 0 && e.getKeyChar() == KeyEvent.VK_SPACE) toggleSelections(); } public void keyTyped(KeyEvent e) { } public void keyReleased(KeyEvent e) { } protected void toggleSelections() { int[] indices = _list.getSelectedIndices(); ListModel model = _list.getModel(); for (int index : indices) { Object element = model.getElementAt(index); if (element instanceof Selectable && ((Selectable) element).isEnabled()) { ((Selectable) element).invertSelected(); boolean selected = ((Selectable) element).isSelected(); _list.fireItemStateChanged(new ItemEvent(_list, ItemEvent.ITEM_STATE_CHANGED, element, selected ? ItemEvent.SELECTED : ItemEvent.DESELECTED)); } } _list.repaint(); } protected void toggleSelection(int index) { ListModel model = _list.getModel(); if (index >= 0) { Object element = model.getElementAt(index); if (element instanceof Selectable && ((Selectable) element).isEnabled()) { ((Selectable) element).invertSelected(); boolean selected = ((Selectable) element).isSelected(); _list.fireItemStateChanged(new ItemEvent(_list, ItemEvent.ITEM_STATE_CHANGED, element, selected ? ItemEvent.SELECTED : ItemEvent.DESELECTED)); } _list.repaint(); } } protected void toggleSelection() { int index = _list.getSelectedIndex(); toggleSelection(index); } } /** * Adds a listener to the list that's notified each time a change to the item selection occurs. Listeners added * directly to the CheckBoxList will have their ItemEvent.getSource() == this * CheckBoxList. * * @param listener the ItemListener to add */ public void addItemListener(ItemListener listener) { listenerList.add(ItemListener.class, listener); } /** * Removes a listener from the list that's notified each time a change to the item selection occurs. * * @param listener the ItemListener to remove */ public void removeItemListener(ItemListener listener) { listenerList.remove(ItemListener.class, listener); } /** * Returns an array of all the ItemListeners added to this JList with addItemListener(). * * @return all of the ItemListeners added or an empty array if no listeners have been added * * @see #addItemListener */ public ItemListener[] getItemListeners() { return listenerList.getListeners(ItemListener.class); } /** * Notifies all listeners that have registered interest for notification on this event type. The event instance is * lazily created using the event parameter. * * @param event the ItemEvent object * @see javax.swing.event.EventListenerList */ protected void fireItemStateChanged(ItemEvent event) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); ItemEvent e = null; // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ItemListener.class) { // Lazily create the event: if (e == null) { e = new ItemEvent(CheckBoxListWithSelectable.this, ItemEvent.ITEM_STATE_CHANGED, event.getItem(), event.getStateChange()); } ((ItemListener) listeners[i + 1]).itemStateChanged(e); } } } /** * Gets the selected objects. This is different from {@link #getSelectedValues()} which is a JList's feature. The * List returned from this method contains the objects that is checked in the CheckBoxList. * * @return the selected objects. */ public Object[] getSelectedObjects() { Vector elements = new Vector(); for (int i = 0; i < getModel().getSize(); i++) { Object elementAt = getModel().getElementAt(i); if (elementAt instanceof Selectable) { Selectable selectable = (Selectable) elementAt; if (selectable.isSelected()) { if (selectable instanceof DefaultSelectable) { elements.add(((DefaultSelectable) selectable).getObject()); } else { elements.add(selectable); } } } } return elements.toArray(); } /** * Selects all objects in this list except those are disabled. */ public void selectAll() { for (int i = 0; i < getModel().getSize(); i++) { Object elementAt = getModel().getElementAt(i); if (elementAt instanceof Selectable) { Selectable selectable = (Selectable) elementAt; if (selectable.isEnabled() && !selectable.isSelected()) { selectable.setSelected(true); fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, selectable, ItemEvent.SELECTED)); } } } repaint(); } /** * Deselects all objects in this list except those are disabled. */ public void selectNone() { for (int i = 0; i < getModel().getSize(); i++) { Object elementAt = getModel().getElementAt(i); if (elementAt instanceof Selectable) { Selectable selectable = (Selectable) elementAt; if (selectable.isEnabled() && selectable.isSelected()) { selectable.setSelected(false); fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, selectable, ItemEvent.DESELECTED)); } } } repaint(); } @Override public void setListData(Vector listData) { super.setListData(wrap(listData)); } @Override public void setListData(Object[] listData) { super.setListData(wrap(listData)); } @Override public int getNextMatch(String prefix, int startIndex, Position.Bias bias) { return -1; } /** * Gets the value of property checkBoxEnabled. If true, user can click on check boxes on each tree node to select * and deselect. If false, user can't click but you as developer can programmatically call API to select/deselect * it. * * @return the value of property checkBoxEnabled. */ public boolean isCheckBoxEnabled() { return _checkBoxEnabled; } /** * Checks if check box is visible. There is no setter for it. The only way is to override this method to return true * or false. * * @param index the row index. * @return true or false. If false, there is not check box on the particular row index. */ @SuppressWarnings({"UnusedDeclaration"}) public boolean isCheckBoxVisible(int index) { return true; } /** * Sets the value of property checkBoxEnabled. * * @param checkBoxEnabled true to enable all the check boxes. False to disable all of them. */ public void setCheckBoxEnabled(boolean checkBoxEnabled) { if (checkBoxEnabled != _checkBoxEnabled) { Boolean oldValue = _checkBoxEnabled ? Boolean.TRUE : Boolean.FALSE; Boolean newValue = checkBoxEnabled ? Boolean.TRUE : Boolean.FALSE; _checkBoxEnabled = checkBoxEnabled; firePropertyChange(PROPERTY_CHECKBOX_ENABLED, oldValue, newValue); repaint(); } } }