/*
 This file is part of the software framework JStatCom, 
 a library for building applications for data based analysis.

 Copyright (C) 2000-2005  Markus Kraetzig

 This library is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as
 published by the Free Software Foundation; either version 2.1 of the
 License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 USA
 */

package com.jstatcom.component;

import java.awt.Component;
import java.awt.Font;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.JCheckBox;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.plaf.FontUIResource;

import org.apache.log4j.Logger;

import com.jstatcom.model.JSCConstants;
import com.jstatcom.model.JSCNArray;
import com.jstatcom.model.JSCSArray;
import com.jstatcom.model.JSCTypeDef;
import com.jstatcom.model.JSCTypes;
import com.jstatcom.model.Scope;
import com.jstatcom.model.Symbol;
import com.jstatcom.model.SymbolEvent;
import com.jstatcom.model.SymbolListener;
import com.jstatcom.model.SymbolTable;
import com.jstatcom.util.UMatrix;

/**
 * This class provides a special JList that renders its items as
 * selectable JCheckBox components. Ctrl-A selects all items,
 * Ctrl-X deselects all.
 * 

* It can be used to provide the user with a selection mechanism for a set of * keys. The objects to select can be set via a JSCSArray object * or a general Object array. If new data is set that contains * elements that have been set before, the current selection is preserved. This * behaviour can be turned off by invoking clear before setting * new data. It is possible to provide a titled border for this list via * setBorderTitle. *

* The contents of this list can be specified by a symbol. In the same way, the * selection state can be stored to another symbol. However, it is not possible * to change the selection state by changing the symbol contents. *

* Whenever there is a change in the selection status by either clicking on the * list checkboxes or by invoking clearSelectionStatus, a * "selectionChanged" property change event is fired. This does not happen if * the selection status changed due to changes in the data model. * * @author Markus Kraetzig */ public final class CheckBoxList extends JList implements SymbolListener { private static final Logger log = Logger.getLogger(CheckBoxList.class); private JSCTypeDef symbolDef = null; private Symbol symbol = null; private String symbolNameSelected = null; private CheckBoxListItem[] currentData = new CheckBoxListItem[0]; private Scope symbolScope = Scope.GLOBAL; /** * The class representing items and its selection state. */ private static final class CheckBoxListItem { private final String name; private final Object value; private boolean selected = false; public CheckBoxListItem(String name, Object value) { this.name = name; this.value = value; } public String getName() { return name; } public Object getValue() { return value; } public boolean isSelected() { return selected; } public void setSelected(boolean newSelected) { selected = newSelected; } } /** * Updates list if symbol changed. */ public void valueChanged(SymbolEvent evt) { symbol = evt.getSource(); setJSCSArray(symbol.getJSCSArray()); } /** * The class doing the rendering. */ private static class CheckBoxListCellRenderer extends JCheckBox implements ListCellRenderer { public CheckBoxListCellRenderer() { super(); } /** * Prepares component to painted. */ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { String s = ((CheckBoxListItem) value).getName(); setBackground(list.getBackground()); setForeground(list.getForeground()); setText(s); setSelected(((CheckBoxListItem) value).isSelected()); setEnabled(list.isEnabled()); setFont(list.getFont()); return this; } } private String fieldBorderTitle = ""; private final HashMap map = new HashMap(); private final MouseEventHandler mouseEventHandler = new MouseEventHandler(); private final KeyEventHandler keyEventHandler = new KeyEventHandler(); private static final CheckBoxListCellRenderer renderer = new CheckBoxListCellRenderer(); class MouseEventHandler extends MouseAdapter { public void mouseClicked(java.awt.event.MouseEvent e) { if (e.getSource() == CheckBoxList.this) checkBoxList_MouseClicked(e); }; }; class KeyEventHandler extends KeyAdapter { public void keyPressed(KeyEvent keyEvent) { if (keyEvent.getSource() != CheckBoxList.this) return; // Ctrl+A KeyEvent, fire selection event as well if (keyEvent.getKeyCode() == KeyEvent.VK_A && keyEvent.getModifiers() == InputEvent.CTRL_MASK) { for (int i = 0; i < currentData.length; i++) currentData[i].setSelected(true); repaint(); storeStatusToSelSymbol(); } else if (keyEvent.getKeyCode() == KeyEvent.VK_X && keyEvent.getModifiers() == InputEvent.CTRL_MASK) { for (int i = 0; i < currentData.length; i++) currentData[i].setSelected(false); repaint(); storeStatusToSelSymbol(); } } }; /** * CheckBoxList default constructor. */ public CheckBoxList() { super(); initialize(); } /** * Overrides addNotify to retrieve data objects as soon as * the component hierarchy is established. * */ public void addNotify() { super.addNotify(); updateSymbol(); } /** * Comment */ private void checkBoxList_MouseClicked(MouseEvent mouseEvent) { int sel = locationToIndex(mouseEvent.getPoint()); if (sel < 0) return; CheckBoxListItem item = (CheckBoxListItem) getModel().getElementAt(sel); item.setSelected(!item.isSelected()); storeStatusToSelSymbol(); // Fire the property change to notify listeners. setSelectionChanged(); repaint(); } /** * Removes all data from the list model and wipes out all selection * information. * */ public void clear() { map.clear(); removeAll(); storeStatusToSelSymbol(); } /** * Deselects all checkboxes in this list and fires a property change event. */ public void clearSelectionStatus() { int[] oldStatus = getIntSelectionStatus(); if (UMatrix.getNonzeroIntCount(oldStatus) == 0) return; // nothing to do for (int i = 0; i < getModel().getSize(); i++) ((CheckBoxListItem) getModel().getElementAt(i)).setSelected(false); storeStatusToSelSymbol(); // Fire the property change to notify listeners only if something was // selected before. setSelectionChanged(); repaint(); } /** * Sets the selection of the checkboxes in this list according to an array * indicating the selection status. The other standard * setSelection methods take no effect. * * @param selIndizes * array indicating whether the checkboxes should be selected or * not (0: no selection, else: selection) * @throws IllegalArgumentException * if (selIndizes == null || selIndizes.length != getModel().getSize()) */ public void setSelectionStatus(int[] selIndizes) { if (selIndizes == null) throw new IllegalArgumentException("Argument was null."); if (selIndizes.length != getModel().getSize()) throw new IllegalArgumentException( "Argument selection array has length " + selIndizes.length + " but model size is " + getModel().getSize() + "."); int[] oldStatus = getIntSelectionStatus(); boolean isEqual = true; for (int i = 0; i < oldStatus.length; i++) { int check = (selIndizes[i] == 0) ? 0 : 1; if (oldStatus[i] != check) { isEqual = false; break; } } if (isEqual) return; // nothing to do for (int i = 0; i < getModel().getSize(); i++) ((CheckBoxListItem) getModel().getElementAt(i)) .setSelected(selIndizes[i] != 0); storeStatusToSelSymbol(); // Fire the property change to notify listeners only if something was // selected before. setSelectionChanged(); repaint(); } /** * Gets the array indicating the selection status of all keys by setting the * corresponding elements to true or false. * Selection here means not list selection, but whether the * JCheckBox for an item is selected. * * @return array with the selection states of the items */ public boolean[] getBoolSelectionStatus() { boolean[] selIndices = new boolean[getModel().getSize()]; for (int i = 0; i < selIndices.length; i++) selIndices[i] = ((CheckBoxListItem) getModel().getElementAt(i)) .isSelected(); return selIndices; } /** * Gets the title of the border displayed arround this list. * * @return the title string * @see #setBorderTitle */ public String getBorderTitle() { return fieldBorderTitle; } /** * Gets the array indicating the selection status of all keys by setting the * corresponding elements to 1 or 0. * Selection here means not list selection, but whether the * JCheckBox for an item is selected. * * @return array with the selection states of the items */ public int[] getIntSelectionStatus() { int[] selIndices = new int[getModel().getSize()]; for (int i = 0; i < selIndices.length; i++) selIndices[i] = ((CheckBoxListItem) getModel().getElementAt(i)) .isSelected() ? 1 : 0; return selIndices; } /** * Gets all elements of this list that are selected. Selection here means * not list selection, but whether the JCheckBox for an item * is selected. * * @return the objects with selection state true */ public Object[] getSelectedObjects() { ArrayList sel = new ArrayList(); for (int i = 0; i < getModel().getSize(); i++) { CheckBoxListItem item = (CheckBoxListItem) getModel().getElementAt( i); if (item.isSelected()) sel.add(item.getValue()); } return sel.toArray(); } /** * Gets the symbol identifier for the data object of the data displayed. * * @return the symbol name */ public String getSymbolName() { return (symbolDef == null) ? null : symbolDef.name; } /** * Gets the symbol identifier for the current selection state. The state is * stored as an int array, where 1 indicates a selection. * * @return the symbol name for the selection state */ public String getSymbolNameSelected() { return symbolNameSelected; } /** * Called whenever the part throws an exception. * * @param exception * Exception */ private void handleException(Throwable exception) { log.error("Unhandled Exception", exception); } /** * Initializes connections */ private void initConnections() throws Exception { this.addMouseListener(mouseEventHandler); this.addKeyListener(keyEventHandler); } /** * Initialize the class. */ /* WARNING: THIS METHOD WILL BE REGENERATED. */ private void initialize() { try { // user code begin {1} // user code end setName("CheckBoxList"); setSize(160, 120); initConnections(); } catch (Throwable ivjExc) { handleException(ivjExc); } // user code begin {2} setCellRenderer(renderer); setListData(currentData); // user code end } /** * Sets the border title of this list. If invoked, an * EtchedBorder with the title defined in * borderTitle is wrapped around this list. The title is * displayed top left. * * @param borderTitle * the title to be displayed, * if ( borderTitle == null || borderTitle.length() == 0 ) * then a simple EtchedBorder is displayed * * @see #getBorderTitle */ public void setBorderTitle(String borderTitle) { String oldValue = fieldBorderTitle; fieldBorderTitle = borderTitle; TitledBorder title = new TitledBorder(new EtchedBorder(), borderTitle, TitledBorder.RIGHT, TitledBorder.TOP); title.setTitleFont(new FontUIResource("dialog", Font.PLAIN, 10)); this.setBorder(title); firePropertyChange("borderTitle", oldValue, borderTitle); } /** * Sets the data that should be displayed in the list. The argument is * specified as an object array. The toString method is * queried for the label of the repective item to display. * * @param values * the object array with the elements to add, it must not be * null * @throws IllegalArgumentException * if (values == null) */ public void setObjArray(Object[] values) { if (values == null) throw new IllegalArgumentException("Object array is null"); CheckBoxListItem listData[] = new CheckBoxListItem[values.length]; List alreadyEncountered = new ArrayList(); // Add new items but preserve previously set selection. for (int i = 0; i < listData.length; i++) { if (values[i] == null) values[i] = "null"; if (!map.containsKey(values[i])) { listData[i] = new CheckBoxListItem(values[i].toString(), values[i]); map.put(values[i], listData[i]); } else if (alreadyEncountered.contains(values[i])) listData[i] = new CheckBoxListItem(values[i].toString(), values[i]); else listData[i] = map.get(values[i]); alreadyEncountered.add(values[i]); } map.clear(); for (int i = 0; i < listData.length; i++) map.put(values[i], listData[i]); currentData = listData; setListData(listData); storeStatusToSelSymbol(); } /** * Sets the data that should be displayed in the list. The argument is * specified as a data object that is queried for the number of rows. The * first element in each row is then added. Typically it contains a * representation of a 1-dimensional string array. The toString * method is queried for the label of the repective item to display. * * @param dataKeys * the data object with the elements to add, it must not be * null * @throws IllegalArgumentException * if (dataKeys == null) */ public void setJSCSArray(JSCSArray dataKeys) { if (dataKeys == null) throw new IllegalArgumentException("Data object is null"); Object obj[] = new Object[dataKeys.rows()]; for (int i = 0; i < obj.length; i++) obj[i] = dataKeys.stringAt(i, 0); setObjArray(obj); } /** * This method is called, whenever the mouse was clicked over an item in the * list and the selection changed. It fires a property change event * "selectionChanged" and can be used by graphical development tools to * easily connect listeners. */ public void setSelectionChanged() { firePropertyChange("selectionChanged", true, false); } /** * Sets the symbol name to retrieve data from. The symbol data is never * overwritten by the data that might have been set before to this list, but * just read from the symbol. * * @param newSymbolName * the symbol name * @throws IllegalArgumentException * if (JSCConstants.isValidName(newSymbolName) != null) */ public void setSymbolName(String newSymbolName) { if (newSymbolName == null) symbolDef = null; else symbolDef = new JSCTypeDef(newSymbolName, JSCTypes.SARRAY); updateSymbol(); } /** * Sets the symbol name to store the selection state to. The selection state * is never retrieved from the symbol object. * * @param newSymbolNameSelected * the symbol name for the selection state * @throws IllegalArgumentException * if (JSCConstants.isValidName(newSymbolNameSelected) != null) */ public void setSymbolNameSelected(String newSymbolNameSelected) { if (newSymbolNameSelected != null) JSCConstants.checkNameThrowEx(newSymbolNameSelected); symbolNameSelected = newSymbolNameSelected; storeStatusToSelSymbol(); } /** * Stores the current selection state to the selection symbol if one has * been set. */ private void storeStatusToSelSymbol() { if (symbolNameSelected == null) return; // Store selection to selection symbol if one is set. SymbolTable sTable = symbolScope.getSymbolTable(this); if (sTable != null) sTable.set(new JSCNArray(symbolNameSelected, getIntSelectionStatus())); } /** * Gets the current symbol scope. * * @return symbol scope */ public Scope getSymbolScope() { return symbolScope; } /** * Updates the display according to the symbol. * */ private void updateSymbol() { if (symbol != null) symbol.removeSymbolListener(this); if (symbolDef != null) { SymbolTable sTable = symbolScope.getSymbolTable(this); if (sTable != null) { symbol = sTable.get(symbolDef); setJSCSArray(symbol.getJSCSArray()); symbol.addSymbolListener(this); return; } } } /** * Sets the symbol scope. * * @param scope * @throws IllegalArgumentException * if (scope == null) */ public void setSymbolScope(Scope scope) { if (scope == null) throw new IllegalArgumentException("Argument was null"); symbolScope = scope; updateSymbol(); } }

Ads help maintain this website.