package org.openswing.swing.client; import java.beans.*; import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.plaf.basic.*; import org.openswing.swing.domains.java.*; import org.openswing.swing.logger.client.*; import org.openswing.swing.util.client.*; /** * <p>Title: OpenSwing Framework</p> * <p>Description: List input control with check-box: its items are retrieved through ClientSettings.getDomain method, * that uses domainId property to identify the correct domain to use. * If this control is linked to a Form, then ListSelectionModel.SINGLE_SELECTION_MODE is allowed: * other selection modes are not permitted. * If this control is not linked to a Form, then it is possible to select more than one item. * </p> * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p> * * <p> This file is part of OpenSwing Framework. * This library is free software; you can redistribute it and/or * modify it under the terms of the (LGPL) Lesser General Public * License as published by the Free Software Foundation; * * GNU LESSER GENERAL PUBLIC LICENSE * Version 2.1, February 1999 * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * The author may be contacted at: * maurocarniel@tin.it</p> * * @author Mauro Carniel * @version 1.0 */ public class CheckBoxListControl extends BaseInputControl implements InputControl,SearchControl,MouseListener { private ArrayList mouseListeners = new ArrayList(); /** flag used in addNotify method */ private boolean firstTime = false; /** list */ private JList list = new JList() { public void addMouseListener(MouseListener listener) { try { if (listener.equals(CheckBoxListControl.this)) { super.addMouseListener(listener); } else { mouseListeners.add(listener); } } catch (Exception ex) { } } }; /** domain related to this input control */ private Domain domain = null; /** domain identifier */ private String domainId = null; /** list model */ private DefaultListModel model = new DefaultListModel(); /** define if in insert mode the list has no item selected; default value: <code>false</code> i.e. the first item is pre-selected */ private boolean nullAsDefaultValue = false; /** define if description in list items must be translated; default value: <code>true</code> */ private boolean translateItemDescriptions = true; /** * Contructor. */ public CheckBoxListControl() { this.setLayout(new GridBagLayout()); this.add(new JScrollPane(list), new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); setOpaque(false); list.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.getKeyCode()==e.VK_CANCEL || e.getKeyCode()==e.VK_BACK_SPACE || e.getKeyCode()==e.VK_DELETE) list.setSelectedIndex(-1); } }); list.setSelectionForeground((Color)UIManager.get("TextField.foreground")); initListeners(); getBindingComponent().addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent e) { if (ClientSettings.VIEW_BACKGROUND_SEL_COLOR && isEnabled()) { getBindingComponent().setBackground(defaultBackgroundColor); } } }); new SearchWindowManager(this); } public final void addNotify() { super.addNotify(); if (!firstTime) { firstTime = true; list.setCellRenderer(new CheckBoxListControlCellRenderer()); } } /** * Set the domain identifier, so the control can retrieve its items from the domain. * @param domainId domain identifier */ public final void setDomainId(String domainId) { this.domainId = domainId; if (Beans.isDesignTime()) return; Domain domain = ClientSettings.getInstance().getDomain(domainId); setDomain(domain); } /** * Set the specified domain and retrieve the list items from the domain. * @param domain domain to use to retrieve the list items */ public final void setDomain(Domain domain) { this.domain = domain; model.removeAllElements(); if (domain!=null) { DomainPair[] pairs = domain.getDomainPairList(); for(int i=0;i<pairs.length;i++) if (translateItemDescriptions) model.addElement(ClientSettings.getInstance().getResources().getResource(pairs[i].getDescription())); else model.addElement(pairs[i].getDescription()); list.setModel(model); list.revalidate(); list.setSelectedIndex(-1); } } /** * @return domain identifier */ public final String getDomainId() { return domainId; } /** * Select an item from the list starting from the specified code (with ListSelectionModel.SINGLE_SELECTION) or * select a list of items starting from the specified java.util.List of codes (without ListSelectionModel.SINGLE_SELECTION) or * @param code used to retrieve the corresponding item and to select that item in the list (with ListSelectionModel.SINGLE_SELECTION) or java.util.List of codes (without ListSelectionModel.SINGLE_SELECTION) */ public final void setValue(Object code) { if (list.getSelectionMode()==ListSelectionModel.SINGLE_SELECTION) { if (code==null) list.setSelectedIndex(-1); if (domain==null) return; DomainPair pair = domain.getDomainPair(code); if (pair!=null) { if (translateItemDescriptions) list.setSelectedValue( ClientSettings.getInstance().getResources().getResource(pair.getDescription()),true ); else list.setSelectedValue( pair.getDescription(),true ); } } else { if (code==null || code instanceof java.util.List && ((java.util.List)code).size()==0) { list.getSelectionModel().clearSelection(); return; } if (domain==null) return; if (code instanceof java.util.List) { java.util.List codes = (java.util.List)code; list.getSelectionModel().clearSelection(); for(int i=0;i<codes.size();i++) for(int j=0;j<domain.getDomainPairList().length;j++) { if (domain.getDomainPairList()[j].getCode().equals(codes.get(i))) list.getSelectionModel().addSelectionInterval(j,j); } } else Logger.error(this.getClass().getName(), "setValue", "You must specify a java.util.List argument type", null); } } /** * Select the list item related to the specified index. * @param index index to retrieve the corresponding item and to select that item in the list */ public final void setSelectedIndex(int index) { list.setSelectedIndex(index); try { list.scrollRectToVisible(list.getCellBounds(index, index)); } catch (Exception ex) { } } /** * Select the list item related to the specified index. * @param indices interval of indices to retrieve the corresponding item and to select that item in the list */ public final void setSelectedIndices(int[] indices) { list.setSelectedIndices(indices); try { list.scrollRectToVisible(list.getCellBounds(indices[0], indices[0])); } catch (Exception ex) { } } /** * Returns the fixed cell height value -- the value specified by setting * the <code>fixedCellHeight</code> property, * rather than that calculated from the list elements. * * @return the fixed cell height, in pixels * @see #setFixedCellHeight */ public final int getFixedCellHeight() { return list.getFixedCellHeight(); } /** * Sets the height of every cell in the list. If <code>height</code> * is -1, cell * heights are computed by applying <code>getPreferredSize</code> * to the <code>cellRenderer</code> component for each list element. * <p> * The default value of this property is -1. * <p> * This is a JavaBeans bound property. * * @param height an integer giving the height, in pixels, for all cells * in this list * @see #getPrototypeCellValue * @see #setFixedCellWidth * @see JComponent#addPropertyChangeListener * @beaninfo * bound: true * attribute: visualUpdate true * description: Defines a fixed cell height when greater than zero. */ public final void setFixedCellHeight(int height) { list.setFixedCellHeight(height); } /** * Returns the fixed cell width value -- the value specified by setting * the <code>fixedCellWidth</code> property, rather than that calculated * from the list elements. * * @return the fixed cell width * @see #setFixedCellWidth */ public final int getFixedCellWidth() { return list.getFixedCellWidth(); } /** * Sets the width of every cell in the list. If <code>width</code> is -1, * cell widths are computed by applying <code>getPreferredSize</code> * to the <code>cellRenderer</code> component for each list element. * <p> * The default value of this property is -1. * <p> * This is a JavaBeans bound property. * * @param width the width, in pixels, for all cells in this list * @see #getPrototypeCellValue * @see #setFixedCellWidth * @see JComponent#addPropertyChangeListener * @beaninfo * bound: true * attribute: visualUpdate true * description: Defines a fixed cell width when greater than zero. */ public final void setFixedCellWidth(int width) { list.setFixedCellWidth(width); } /** * Returns the preferred number of visible rows. * * @return an integer indicating the preferred number of rows to display * without using a scroll bar * @see #setVisibleRowCount */ public final int getVisibleRowCount() { return list.getVisibleRowCount(); } /** * Sets the preferred number of rows in the list that can be displayed * without a scrollbar, as determined by the nearest * <code>JViewport</code> ancestor, if any. * The value of this property only affects the value of * the <code>JList</code>'s <code>preferredScrollableViewportSize</code>. * <p> * The default value of this property is 8. * <p> * This is a JavaBeans bound property. * * @param visibleRowCount an integer specifying the preferred number of * visible rows * @see #getVisibleRowCount * @see JComponent#getVisibleRect * @see JViewport * @beaninfo * bound: true * attribute: visualUpdate true * description: The preferred number of cells that can be displayed without a scroll bar. */ public final void setVisibleRowCount(int visibleRowCount) { list.setVisibleRowCount(visibleRowCount); } /** * Returns <code>JList.VERTICAL</code> if the layout is a single * column of cells, or <code>JList.VERTICAL_WRAP</code> if the layout * is "newspaper style" with the content flowing vertically then * horizontally or <code>JList.HORIZONTAL_WRAP</code> if the layout is * "newspaper style" with the content flowing horizontally then * vertically. * * @return the value of the layoutOrientation property * @see #setLayoutOrientation * @since 1.4 */ public final int getLayoutOrientation() { return list.getLayoutOrientation(); } /** * Defines the way list cells are layed out. Consider a <code>JList</code> * with four cells, this can be layed out in one of the following ways: * <pre> * 0 * 1 * 2 * 3 * </pre> * <pre> * 0 1 * 2 3 * </pre> * <pre> * 0 2 * 1 3 * </pre> * <p> * These correspond to the following values: * * <table border="1" * summary="Describes layouts VERTICAL, HORIZONTAL_WRAP, and VERTICAL_WRAP"> * <tr><th><p align="left">Value</p></th><th><p align="left">Description</p></th></tr> * <tr><td><code>JList.VERTICAL</code> * <td>The cells should be layed out vertically in one column. * <tr><td><code>JList.HORIZONTAL_WRAP</code> * <td>The cells should be layed out horizontally, wrapping to * a new row as necessary. The number * of rows to use will either be defined by * <code>getVisibleRowCount</code> if > 0, otherwise the * number of rows will be determined by the width of the * <code>JList</code>. * <tr><td><code>JList.VERTICAL_WRAP</code> * <td>The cells should be layed out vertically, wrapping to a * new column as necessary. The number * of rows to use will either be defined by * <code>getVisibleRowCount</code> if > 0, otherwise the * number of rows will be determined by the height of the * <code>JList</code>. * </table> * The default value of this property is <code>JList.VERTICAL</code>. * <p> * This will throw an <code>IllegalArgumentException</code> if * <code>layoutOrientation</code> is not one of * <code>JList.HORIZONTAL_WRAP</code> or <code>JList.VERTICAL</code> or * <code>JList.VERTICAL_WRAP</code> * * @param layoutOrientation New orientation, one of * <code>JList.HORIZONTAL_WRAP</code>, <code>JList.VERTICAL</code> * or <code>JList.VERTICAL_WRAP</code>. * @see #getLayoutOrientation * @see #setVisibleRowCount * @see #getScrollableTracksViewportHeight * @since 1.4 * @beaninfo * bound: true * attribute: visualUpdate true * description: Defines the way list cells are layed out. * enum: VERTICAL JList.VERTICAL * HORIZONTAL_WRAP JList.HORIZONTAL_WRAP * VERTICAL_WRAP JList.VERTICAL_WRAP */ public final void setLayoutOrientation(int layoutOrientation) { list.setLayoutOrientation(layoutOrientation); } /** * Determines whether single-item or multiple-item * selections are allowed. * The following <code>selectionMode</code> values are allowed: * <ul> * <li> <code>ListSelectionModel.SINGLE_SELECTION</code> * Only one list index can be selected at a time. In this * mode the <code>setSelectionInterval</code> and * <code>addSelectionInterval</code> * methods are equivalent, and only the second index * argument is used. * <li> <code>ListSelectionModel.SINGLE_INTERVAL_SELECTION</code> * One contiguous index interval can be selected at a time. * In this mode <code>setSelectionInterval</code> and * <code>addSelectionInterval</code> * are equivalent. * <li> <code>ListSelectionModel.MULTIPLE_INTERVAL_SELECTION</code> * In this mode, there's no restriction on what can be selected. * This is the default. * </ul> * * @param selectionMode an integer specifying the type of selections * that are permissible * @see #getSelectionMode * @beaninfo * description: The selection mode. * enum: SINGLE_SELECTION ListSelectionModel.SINGLE_SELECTION * SINGLE_INTERVAL_SELECTION ListSelectionModel.SINGLE_INTERVAL_SELECTION * MULTIPLE_INTERVAL_SELECTION ListSelectionModel.MULTIPLE_INTERVAL_SELECTION */ public final void setSelectionMode(int selectionMode) { list.setSelectionMode(selectionMode); } /** * Returns whether single-item or multiple-item selections are allowed. * * @return the value of the <code>selectionMode</code> property * @see #setSelectionMode */ public final int getSelectionMode() { return list.getSelectionMode(); } /** * Sets the data model's <code>isAdjusting</code> property to true, * so that a single event will be generated when all of the selection * events have finished (for example, when the mouse is being * dragged over the list in selection mode). * * @param b the boolean value for the property value * @see ListSelectionModel#setValueIsAdjusting */ public final void setValueIsAdjusting(boolean b) { list.setValueIsAdjusting(b); } /** * Returns the value of the data model's <code>isAdjusting</code> property. * This value is true if multiple changes are being made. * * @return true if multiple selection-changes are occurring, as * when the mouse is being dragged over the list * @see ListSelectionModel#getValueIsAdjusting */ public final boolean getValueIsAdjusting() { return list.getValueIsAdjusting(); } /** * Returns the selection foreground color. * * @return the <code>Color</code> object for the foreground property * @see #setSelectionForeground * @see #setSelectionBackground */ public Color getSelectionForeground() { return list.getSelectionForeground(); } /** * Sets the foreground color for selected cells. Cell renderers * can use this color to render text and graphics for selected * cells. * <p> * The default value of this property is defined by the look * and feel implementation. * <p> * This is a JavaBeans bound property. * * @param selectionForeground the <code>Color</code> to use in the foreground * for selected list items * @see #getSelectionForeground * @see #setSelectionBackground * @see #setForeground * @see #setBackground * @see #setFont * @beaninfo * bound: true * attribute: visualUpdate true * description: The foreground color of selected cells. */ public final void setSelectionForeground(Color selectionForeground) { list.setSelectionForeground(selectionForeground); } /** * Returns the background color for selected cells. * * @return the <code>Color</code> used for the background of * selected list items * @see #setSelectionBackground * @see #setSelectionForeground */ public final Color getSelectionBackground() { return list.getSelectionBackground(); } /** * Sets the background color for selected cells. Cell renderers * can use this color to the fill selected cells. * <p> * The default value of this property is defined by the look * and feel implementation. * <p> * This is a JavaBeans bound property. * * @param selectionBackground the <code>Color</code> to use for the * background of selected cells * @see #getSelectionBackground * @see #setSelectionForeground * @see #setForeground * @see #setBackground * @see #setFont * @beaninfo * bound: true * attribute: visualUpdate true * description: The background color of selected cells. */ public void setSelectionBackground(Color selectionBackground) { list.setSelectionBackground(selectionBackground); } /** * Adds a listener to the list that's notified each time a change * to the selection occurs. Listeners added directly to the * <code>JList</code> * will have their <code>ListSelectionEvent.getSource() == * this JList</code> * (instead of the <code>ListSelectionModel</code>). * * @param listener the <code>ListSelectionListener</code> to add * @see #getSelectionModel * @see #getListSelectionListeners */ public final void addListSelectionListener(ListSelectionListener listener) { list.addListSelectionListener(listener); } /** * Removes a listener from the list that's notified each time a * change to the selection occurs. * * @param listener the <code>ListSelectionListener</code> to remove * @see #addListSelectionListener * @see #getSelectionModel */ public final void removeListSelectionListener(ListSelectionListener listener) { list.removeListSelectionListener(listener); } /** * @return code or java.util.List of codes related to the selected item/items; it return null if no item is selected (with ListSelectionModel.SINGLE_SELECTION) or an empty java.util.List (without ListSelectionModel.SINGLE_SELECTION) */ public final Object getValue() { if (list.getSelectionMode()==ListSelectionModel.SINGLE_SELECTION) { if (domain==null) return null; DomainPair[] pairs = domain.getDomainPairList(); if (list.getSelectedIndex()==-1) return null; return pairs[list.getSelectedIndex()].getCode(); } else { if (domain==null) return new ArrayList(); DomainPair[] pairs = domain.getDomainPairList(); ArrayList codes = new ArrayList(); int[] values = list.getSelectedIndices(); for(int j=0;j<values.length;j++) codes.add(pairs[values[j]].getCode()); return codes; } } public void setEnabled(boolean enabled) { list.setEnabled(enabled); list.setFocusable(enabled || ClientSettings.DISABLED_INPUT_CONTROLS_FOCUSABLE); if (enabled) { list.setBackground( (Color) UIManager.get("TextField.background")); list.setSelectionBackground(ClientSettings.BACKGROUND_SEL_COLOR); } else { list.setBackground( (Color) UIManager.get("TextField.inactiveBackground")); try { list.setSelectionBackground(new Color( list.getBackground().getRed() - 10, list.getBackground().getGreen() - 10, list.getBackground().getBlue() - 10 )); } catch (Exception ex) { list.setSelectionBackground(Color.lightGray); } } } /** * @return current input control abilitation */ public final boolean isEnabled() { try { return list.isEnabled(); } catch (Exception ex) { return false; } } /** * @return component inside this whose contains the value */ public JComponent getBindingComponent() { // if (list!=null && list.getSelectionMode()!=ListSelectionModel.SINGLE_SELECTION) // list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); return list; } /** * Adds the specified focus listener to receive focus events from * this component when this component gains input focus. * If listener <code>l</code> is <code>null</code>, * no exception is thrown and no action is performed. * * @param l the focus listener * @see java.awt.event.FocusEvent * @see java.awt.event.FocusListener * @see #removeFocusListener * @see #getFocusListeners * @since JDK1.1 */ public final void addFocusListener(FocusListener l) { try { list.addFocusListener(l); } catch (Exception ex) { } } /** * Removes the specified focus listener so that it no longer * receives focus events from this component. This method performs * no function, nor does it throw an exception, if the listener * specified by the argument was not previously added to this component. * If listener <code>l</code> is <code>null</code>, * no exception is thrown and no action is performed. * * @param l the focus listener * @see java.awt.event.FocusEvent * @see java.awt.event.FocusListener * @see #addFocusListener * @see #getFocusListeners * @since JDK1.1 */ public final void removeFocusListener(FocusListener l) { try { list.removeFocusListener(l); } catch (Exception ex) { } } /** * Adds the specified mouse listener to receive * mouse events from this textfield. * * @param l the mouse listener to be added */ public final void addMouseListener(MouseListener l) { try { list.addMouseListener(l); } catch (Exception ex) { } } /** * Removes the specified mouse listener so that it no longer * receives mouse events from this textfield. * * @param l the mouse listener to be removed */ public final void removeMouseListener(MouseListener l) { try { list.removeMouseListener(l); } catch (Exception ex) { } } /** * @return define if in insert mode list has no item selected */ public final boolean isNullAsDefaultValue() { return nullAsDefaultValue; } /** * Define if in insert mode list has no item selected. * @param nullAsDefaultValue define if in insert mode list has no item selected */ public final void setNullAsDefaultValue(boolean nullAsDefaultValue) { this.nullAsDefaultValue = nullAsDefaultValue; } /** * @return domain to use to retrieve the list items */ public final Domain getDomain() { return domain; } /** * @return the selected index in the input control */ public final int getSelectedIndex() { return list.getSelectedIndex(); } /** * @return total rows count in the input control */ public final int getRowCount() { return list.getModel().getSize(); } /** * @return the element at the specified index, converted in String format */ public final String getValueAt(int index) { return list.getModel().getElementAt(index)==null?"":list.getModel().getElementAt(index).toString(); } /** * @return combo control */ public final JComponent getComponent() { return list; } /** * @return <code>true</code> if the input control is in read only mode (so search is enabled), <code>false</code> otherwise */ public final boolean isReadOnly() { return !isEnabled(); } /** * <p>Title: OpenSwing Framework</p> * <p>Description: Inner class used to render an item list.</p> * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p> * @author Mauro Carniel * @version 1.0 */ class CheckBoxListControlCellRenderer extends DefaultListCellRenderer { /** check-box showed when CheckBoxListControl.isShowCheckBoxes is true */ private CheckBoxLabel checkBox = new CheckBoxLabel(); /** panel that contains check-box, image and description */ private JPanel panel = new JPanel(); public CheckBoxListControlCellRenderer() { checkBox.setOpaque(false); panel.setOpaque(false); panel.setLayout(new BorderLayout(0, 0)); panel.add(this, BorderLayout.CENTER); checkBox.setSize(14,14); checkBox.setPreferredSize(new Dimension(14,14)); panel.add(checkBox, BorderLayout.BEFORE_LINE_BEGINS); if (list.getSelectionMode()==ListSelectionModel.SINGLE_INTERVAL_SELECTION) list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); list.addMouseListener(CheckBoxListControl.this); } public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Component c = super.getListCellRendererComponent(list,value,index,false,cellHasFocus); checkBox.setEnabled(CheckBoxListControl.this.isEnabled()); checkBox.setSelected(isSelected); return panel; } } /** * <p>Title: OpenSwing Framework</p> * <p>Description: Inner class used to render the check-box.</p> * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p> * <p> </p> * @author Mauro Carniel * @version 1.0 */ class CheckBoxLabel extends JLabel { private boolean sel; public void setSelected(boolean sel) { this.sel = sel; repaint(); } public void paintComponent(Graphics g) { super.paintComponent(g); g.translate((int)this.getWidth()/2-6,this.getHeight()/2-5); BasicGraphicsUtils.drawLoweredBezel(g,0,0,12,12,Color.darkGray,Color.black,Color.white,Color.gray); if (sel) { if (!isEnabled()) g.setColor(Color.GRAY); else g.setColor(Color.black); g.drawLine(3,5,5,7); g.drawLine(3,6,5,8); g.drawLine(3,7,5,9); g.drawLine(6,6,9,3); g.drawLine(6,7,9,4); g.drawLine(6,8,9,5); } } } // end inner class... public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseClicked(MouseEvent e) { if (isEnabled()) { try { int row = list.locationToIndex(e.getPoint()); if (row!=-1) { if (list.getSelectionMode()==ListSelectionModel.SINGLE_SELECTION) { list.setSelectedIndex(row); } else { int[] indexes = list.getSelectedIndices(); for(int i=0;i<indexes.length;i++) if (indexes[i]==row) { list.getSelectionModel().removeSelectionInterval(row,row); return; } list.getSelectionModel().addSelectionInterval(row,row); } } } catch (Exception ex) { } } } /** * @return <code>true</code> to disable key listening on input control (for instance, in case of nested grids), <code>false</code> to listen for key events */ public final boolean disableListener() { return false; } /** * @return define if description in list items must be translated */ public final boolean isTranslateItemDescriptions() { return translateItemDescriptions; } /** * Define if description in list items must be translated; default value: <code>true</code>. * @param translateItemDescriptions flag used to define if description in list items must be translated */ public final void setTranslateItemDescriptions(boolean translateItemDescriptions) { this.translateItemDescriptions = translateItemDescriptions; } /** * Method invoked by SearchWindowManager when the specified "textToSeach" pattern has not matchings in the current content * of binded control. * This callback can be used to retrieve additional data into control and to search inside new data. * @param textToSeach patten of text to search * @return -1 if no additional data is available, otherwise the row index of data just added that satify the pattern */ public final int search(String textToSeach) { return -1; } }