JComboBox renderer closes after each selection
Hello,
This follows my previous post regarding "How to add JCheckBox objects to JComboBox". With Ice's help, I've managed to make some good progress. So now I'm having other issues as I go further.
In the code below, I have the following issues:
1. My JComboBox renderer closes after each selection and I have to expand it again. I want my renderer to stay open until I'm done making selections and choose to close the renderer from the drop-down arrow in myComboBox.
2. At the moment, when I click on an item in my renderer, it gets selected (which is fine), but when I click on the box itself, it won't select/unselect.
Here's my SSCCE. Could Someone please help me out?
/*MyComboBoxTest.java*/
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JComboBox;
publicclass MyComboBoxTestextends JComboBox
{
/**
* @param args
*/
JComboBox myComboBox;
public MyComboBoxTest()
{
myComboBox =new JComboBox();
}
publicstaticvoid main(String[] args){
final MyComboBoxTest myComboBox =new MyComboBoxTest();
myComboBox.getModel().setSelectedItem("Keywords");
myComboBox.insertItemAt("Select All", 0);
myComboBox.insertItemAt("Item1", 1);
myComboBox.insertItemAt("Item2", 2);
myComboBox.insertItemAt("Item3", 3);
myComboBox.insertItemAt("Item4", 4);
JFrame frame =new JFrame("My ComboBox");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(150, 60);
frame.setLocation(300, 300);
frame.getContentPane().add(myComboBox);
frame.setVisible(true);
final IconedCellRenderer renderer = IconedCellRenderer.getCheckBoxRendererInstance();
renderer.setPaintDivider(true);
myComboBox.setRenderer( renderer );
myComboBox.addMouseListener(new MouseAdapter()
{
publicvoid mouseReleased(MouseEvent e)
{
for(int i=0;i<renderer.getSelStateList().length;i++)
{
System.out.println("renderer.getSelStateList["+i+"]: "+renderer.getSelStateList()[i]);
}
}
});
myComboBox.addActionListener(new ActionListener()
{
publicvoid actionPerformed(ActionEvent e)
{
System.out.println("Action Performed");
renderer.setVisible(true);
boolean isSelected = renderer.getSelStateList()[myComboBox.getSelectedIndex()];
if(isSelected==true)
{
if(myComboBox.getSelectedIndex()==0)
{
renderer.setSelStateList(null);
}
else
{
renderer.getSelStateList()[myComboBox.getSelectedIndex()]=false;
renderer.getSelStateList()[0]=false;
}
}
if(isSelected==false)
{
if(myComboBox.getSelectedIndex()==0)
{
for(int i=0;i<renderer.getSelStateList().length;i++)
{
renderer.getSelStateList()[i]=true;
}
}
else
{
renderer.getSelStateList()[myComboBox.getSelectedIndex()]=true;
}
}
}
});
}
}
And here's IconedCellRenderer, implemented by ICE, w/ minor changes from me, that I use as my JComboBox renderer.
/* IconedCellRenderer.java */
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
publicclass IconedCellRendererextends DefaultListCellRendererimplements MouseListener{
public Icon icon, selIcon;
Icon[] icons =null;
publicboolean useIconBackground = true, useIndexSensitiveIcons = false,
useCheckBoxAsIcon = false, useLinkState =false;
Dimension labelDim =null;
public JLabel iconLabel;
public JCheckBox box =null;
public JPanel noback;
public SelectionStateHandler selStateHandler =null;
private JList theList =null;
publicboolean[] selState = null, enableState =null;
int offset = 5;
Rectangle rect =null;
int currentLinkRow = -1;
boolean isOnRow = false, paintDivider =false;
private Icon dividerImage =null;
private Color linkColor = Color.gray, hoverColor = Color.red, selectedLinkColor = Color.green;
/* Initialises the renderer with one icon that is displayed without the
* cell background.
**/
public IconedCellRenderer(Icon icon){
this(icon,false);
}
/* Initialises the renderer with two icons that provide a switch capability
* when a row is selected/deselected
*/
public IconedCellRenderer(Icon icon, Icon selIcon){
this.icon = icon;
this.selIcon = selIcon;
//addMouseListener(this);
}
/* Initialises the renderer with two icons that provide a switch capability
* when a row is selected/deselected. The boolean argument enables the icon
* to either use the renderer background or appear transparent.
*/
public IconedCellRenderer(Icon icon, Icon selIcon,boolean useIconBackground){
this(icon, selIcon);
setIconHasBackground(useIconBackground);
createNoBackgroundPanel();
}
/* Initialises the renderer with a single no siwthing icon. The boolean
* argument enables the icon to either use the renderer background
* or appear transparent.
*/
public IconedCellRenderer(Icon icon,boolean useIconBackground){
this(icon, icon, useIconBackground);
}
/* Initialises the renderer to load two icons from the provided image locations.
* This enables icon switching on selection.
*/
public IconedCellRenderer(String iconLoc, String selIconLoc){
icon =new ImageIcon(iconLoc);
selIcon =new ImageIcon(selIconLoc);
//addMouseListener(this);
}
/* Initialises the renderer to load a single icon from the provided image location.
* The icon can either have the renderer background or not based on the
* boolean property.
*/
public IconedCellRenderer(String iconLoc,boolean iconBackground){
this(iconLoc, iconLoc, iconBackground);
}
/* Initialises the renderer to load two icons from the provided image locations.
* This enables icon switching on selection. The icon can either have the renderer
* background or not based on the boolean property.
*/
public IconedCellRenderer(String iconLoc, String selIconLoc,boolean iconBackground){
this(iconLoc, selIconLoc);
setIconHasBackground(iconBackground);
createNoBackgroundPanel();
}
/* Initialises the renderer to load a single icon from the provided image location.
*/
public IconedCellRenderer(String iconLoc){
this(iconLoc,true);
}
/* Initialises the renderer with an array of image icons that are repeated for
* each row in the list.
*/
public IconedCellRenderer(Icon[] icons,boolean useIconBackground){
this(icons[0], icons[0], useIconBackground);
}
publicvoid createNoBackgroundPanel(){
iconLabel =new JLabel((Icon)null, JLabel.CENTER);
if(labelDim !=null){ iconLabel.setPreferredSize(labelDim);}
iconLabel.setBorder(new EmptyBorder(1,5,1,5) );
noback =new JPanel(new BorderLayout() ){
/**
* Overridden for performance reasons.
* See the ><a href="#override">Implementation Note</a>
* for more information.
*/
//public void validate() {}
// public void invalidate() {}
publicvoid repaint(){}
//public void revalidate() {}
publicvoid repaint(long tm,int x,int y,int width,int height){}
publicvoid repaint(Rectangle r){}
protectedvoid firePropertyChange(String propertyName, Object oldValue, Object newValue){
// Strings get interned...
if (propertyName =="text"
|| ((propertyName =="font" || propertyName =="foreground")
&& oldValue != newValue)){
super.firePropertyChange(propertyName, oldValue, newValue);
}
}
publicvoid firePropertyChange(String propertyName,byte oldValue,byte newValue){}
publicvoid firePropertyChange(String propertyName,char oldValue,char newValue){}
publicvoid firePropertyChange(String propertyName,short oldValue,short newValue){}
publicvoid firePropertyChange(String propertyName,int oldValue,int newValue){}
publicvoid firePropertyChange(String propertyName,long oldValue,long newValue){}
publicvoid firePropertyChange(String propertyName,float oldValue,float newValue){}
publicvoid firePropertyChange(String propertyName,double oldValue,double newValue){}
publicvoid firePropertyChange(String propertyName,boolean oldValue,boolean newValue){}
};
if(useCheckBoxAsIcon()){
box =new JCheckBox();
box.setOpaque(false);
noback.add( box, BorderLayout.WEST );
rect = box.getBounds();
}else{
noback.add( iconLabel, BorderLayout.WEST );
}
noback.add( this, BorderLayout.CENTER );
noback.setBorder(new EmptyBorder(1,1,1,1) );
noback.setOpaque(false);
}
public Component getListCellRendererComponent(JList list, Object value,int index,
boolean isSelected,boolean cellHasFocus){
if(theList ==null || theList != list){
theList = list;
if(useLinkState){
attachLinkSimulationListener();
}
}
setOpaque(true);
setText( value ==null ?"" : value.toString());
if(useIndexSensitiveIcons){
icon = getIcon(index);
}
if(useIconBackground)
setIcon(icon);
setFont( list.getFont() );
setToolTipText(value.toString() );
if(list.isEnabled())
setEnabled( isEnabled(index) );
else
setEnabled( list.isEnabled() );
if(isSelected && isEnabled(index) ){
setForeground( Color.black );
setBackground(new Color(2250, 214, 138) );
if(useIndexSensitiveIcons){
selIcon = getIcon(index);
}
if(useIconBackground)
setIcon(selIcon);
}else{
setForeground(Color.black);
setBackground(Color.white);
setBorder(null);
}
if(cellHasFocus){
setBorder(new CompoundBorder(new LineBorder(new Color(150, 150, 220) ),
new EmptyBorder(2,2,2,2) ) );
}
if(useLinkState){
if(currentLinkRow == index){
setText("<html><u>" + value.toString() +"</u></html>" );
setForeground( getHoverLinkColor() );
}else{
setForeground( getLinkColor() );
}
if(isSelected){
setForeground( getSelectedLinkColor() );
}
setBackground(Color.white);
setBorder(new EmptyBorder(1,1,1,1) );
}
if( shdPaintDivider() ){
Border border =null;
if(index==1)
{
if(dividerImage !=null){
border =new MatteBorder(1,0,0,0, dividerImage);
}else{
border =new MatteBorder(1,0,0,0, getLinkColor() );
}
if(index < theList.getModel().getSize() - 1 ){
setBorder(new CompoundBorder(getBorder(),border) );
}else{
setBorder(new EmptyBorder(1,1,1,1) );
}
}
}
if(useIconBackground ==false){
if(isSelected)
iconLabel.setIcon(selIcon);
else
iconLabel.setIcon(icon);
if(useCheckBoxAsIcon()){
if(selState ==null){
updateSelectionStateTrackers(list);
}
if(selStateHandler ==null){
list.addMouseListener( selStateHandler =new SelectionStateHandler(list) );
}
//if selStateHandler is not null
try{
box.setSelected( selState[index] );
setSelStateList(selState);
}catch(Exception e){}
}
if( shdPaintDivider() ){
//if(index < theList.getModel().getSize() - 1 ) {
noback.setBorder( getBorder() );
setBorder(new EmptyBorder(1,1,1,1) );
//} else {
// noback.get
//}
}
// this should cause a JComboBox to paint the Label instead of the
// check box + label combination
if(index == -1){
JLabel label =new JLabel( this.getText() );
if(iconLabel.getIcon() !=null){
label.setIcon( iconLabel.getIcon() );
}
return label;
}
return noback;
}
returnthis;
}
publicvoid updateSelectionStateTrackers(JList list){
selState =newboolean[ list.getModel().getSize() ];
enableState =newboolean[ list.getModel().getSize() ];
for(int i = 0; i < selState.length; i++){
selState[i] =false;
enableState[i] =true;
setSelStateList(selState);
}
}
publicint[] getSelectedIndices(){
if(!useCheckBoxAsIcon()){
returnnewint[0];
}
int length = 0;
for(int i = 0; i < selState.length; i++){
if(selState[i]){
length++;
setSelStateList(selState);
}
}
int[] indices =newint[length];
for(int i = 0, n = 0; i < selState.length; i++){
if(selState[i]){
indices[n++] = i;
setSelStateList(selState);
}
}
//System.out.println("Selected Indices.length = " + indices.length);
return indices;
}
public Vector getSelectedObjects(){
int[] indices = getSelectedIndices();
Vector objects =new Vector();
for(int i = 0; i < indices.length; i++){
objects.addElement( theList.getModel().getElementAt(indices[i]) );
}
return objects;
}
publicvoid setIconHasBackground(boolean b){
useIconBackground = b;
}
public Icon[] getIcons(){
return icons;
}
public Icon getIcon(int index){
if(icons !=null && icons.length == 0){
return icon;
}
if(icons !=null && index > icons.length){
index = index - (icons.length - 1);
}
return icons[index];
}
publicvoid setIcons(Icon[] icons){
if(icons !=null){
useIndexSensitiveIcons =true;
}
this.icons = icons;
}
publicvoid setIcon(Icon icon,int index){
if(icons !=null && icons.length > 0){
icons[index] = icon;
}
}
publicvoid setIconLabelDimension(Dimension dim){
labelDim = dim;
}
publicstatic IconedCellRenderer getCheckBoxRendererInstance(){
IconedCellRenderer cr =new IconedCellRenderer(new EmptyIcon());
cr.setUseCheckBoxAsIcon(true);
return cr;
}
publicvoid setUseCheckBoxAsIcon(boolean use){
useCheckBoxAsIcon = use;
createNoBackgroundPanel();
}
publicboolean useCheckBoxAsIcon(){
return useCheckBoxAsIcon;
}
publicvoid paintComponent(Graphics g){
super.paintComponent(g);
}
publicvoid setDisplayItemsAsLinks(boolean use){
useLinkState = use;
}
publicvoid setLinkColor(Color color){
linkColor = color;
if(theList !=null){
theList.repaint();
}
}
public Color getLinkColor(){
return linkColor;
}
publicvoid setHoverLinkColor(Color color){
hoverColor = color;
if(theList !=null){
theList.repaint();
}
}
public Color getHoverLinkColor(){
return hoverColor;
}
publicvoid setSelectedLinkColor(Color color){
selectedLinkColor = color;
if(theList !=null){
theList.repaint();
}
}
public Color getSelectedLinkColor(){
return selectedLinkColor;
}
publicvoid attachLinkSimulationListener(){
theList.setCursor( Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) );
theList.addMouseListener(new MouseAdapter(){
publicvoid mouseEntered(MouseEvent e){
isOnRow =true;
}
publicvoid mouseExited(MouseEvent e){
isOnRow =false;
currentLinkRow = -1;
theList.repaint();
}
});
theList.addMouseMotionListener(new MouseMotionAdapter(){
publicvoid mouseMoved(MouseEvent e){
isOnRow =true;
currentLinkRow = theList.locationToIndex( e.getPoint() );
theList.repaint();
}
});
/*table.addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
isOnRow = false;
currentHighlightRow = -1;
table.repaint();
}
});
table.addMouseMotionListener( new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
isOnRow = true;
currentHighlightRow = table.rowAtPoint( e.getPoint() );
table.repaint();
}
});*/
}
publicboolean shdPaintDivider(){
return paintDivider;
}
publicvoid setPaintDivider(boolean paintDivider){
this.paintDivider = paintDivider;
}
publicvoid setDividerImage(Icon icon){
this.dividerImage = icon;
setPaintDivider(true);
}
privatevoid dispatchEvent(MouseEvent me){
if(rect !=null && box !=null && rect.contains(me.getX(), me.getY())){
Point pt = me.getPoint();
pt.translate(0,0);
box.setBounds(rect);
box.dispatchEvent(new MouseEvent(box, me.getID()
, me.getWhen(), me.getModifiers()
, pt.x, pt.y, me.getClickCount()
, me.isPopupTrigger(), me.getButton()));
if(!box.isValid()){
repaint();
System.out.println("Dispatch Event: Box.invalid called");
}
System.out.println("Dispatch Event called");
}else{
System.out.println("Dispatch Event Called, rect null");
}
}
publicvoid mouseClicked(MouseEvent me){
dispatchEvent(me);
}
publicvoid mouseEntered(MouseEvent me){
dispatchEvent(me);
}
publicvoid mouseExited(MouseEvent me){
dispatchEvent(me);
}
publicvoid mousePressed(MouseEvent me){
dispatchEvent(me);
}
publicvoid mouseReleased(MouseEvent me){
dispatchEvent(me);
}
publicclass SelectionStateHandlerextends MouseAdapter{
JList list =null;
public SelectionStateHandler(JList list){
this.list = list;
/*for(int i=0;i<list.getModel().getSize();i++)
{
System.out.println(list.getModel().getElementAt(i));
}*/
}
/*
* Handles the checkbox selection process. Uses the bounds property of the
* check box within the selected cell to determine whether the checkbox should
* be selected or not
**/
publicvoid mouseReleased(MouseEvent e){
if(list ==null || list.getSelectedIndex() == -1
|| !isEnabled( list.locationToIndex(e.getPoint()) ) ){
return;
}
int[] indices = list.getSelectedIndices();
// get the current relative position of the check box
//rect = box.getBounds(rect);
for(int i = 0; i >< indices.length; i++){
// get the current relative position of the check box
int loc = list.locationToIndex( e.getPoint() );
rect = list.getCellBounds(loc,loc);
// ensure the point clicked in within the checkBox
if(e.getX() < (rect.getX() + 20) ){
selState[indices[i]] = !selState[indices[i]];
setSelStateList(selState);
}
}
list.revalidate();
list.repaint();
}
publicvoid selectAll(boolean b){
for(int i = 0; i < list.getModel().getSize(); i++){
try{
selState[i] = b;
}catch(ArrayIndexOutOfBoundsException aie){
updateSelectionStateTrackers(list);
selectAll(b);
return;
}
}
if(list !=null){
list.revalidate();
list.repaint();
}
}
publicvoid setSelectedIndex(int index){
for(int i = 0; i < list.getModel().getSize(); i++){
selState[i] =false;
}
setSelStateList(selState);
selectIndex(index);
}
publicvoid selectIndex(int index){
try{
selState[index] =true;
setSelStateList(selState);
}catch(ArrayIndexOutOfBoundsException aie){
updateSelectionStateTrackers(list);
selectIndex(index);
return;
}
if(list !=null){
list.revalidate();
list.repaint();
}
}
publicvoid setEnabled(int index,boolean b){
try{
enableState[index] = b;
}catch(ArrayIndexOutOfBoundsException aie){
updateSelectionStateTrackers(list);
setEnabled(index, b);
}
}
publicboolean isEnabled(int index){
if(index == -1){
returntrue;
}
boolean isEnabled =true;
try{
isEnabled = enableState[index];
}catch(ArrayIndexOutOfBoundsException aie){
updateSelectionStateTrackers(list);
return isEnabled(index);
}
return isEnabled;
}
publicvoid enableAll(boolean b){
for(int i = 0; i < enableState.length; i++){
enableState[i] = b;
}
}
}
publicvoid selectAll(boolean b){
if(selStateHandler ==null){
return;
}
selStateHandler.selectAll(b);
}
publicvoid setSelectedIndex(int index){
if(selStateHandler ==null){
return;
}
selStateHandler.setSelectedIndex(index);
}
publicvoid selectIndex(int index){
if(selStateHandler ==null){
return;
}
selStateHandler.selectIndex(index);
}
publicvoid enableAll(boolean b){
if(selStateHandler ==null){
return;
}
selStateHandler.enableAll(b);
}
publicvoid setEnabled(int index,boolean enable){
if(selStateHandler ==null){
return;
}
selStateHandler.setEnabled(index, enable);
}
publicboolean isEnabled(int index){
if(selStateHandler ==null){
returntrue;
}
return selStateHandler.isEnabled(index);
}
publicboolean isEnabledAll(){
if(enableState ==null)returntrue;
for(int i = 0; i < enableState.length; i++){
if(!isEnabled(i)){
returnfalse;
}
}
returntrue;
}
publicvoid setSelStateList(boolean [] selState)
{
this.selState = selState;
}
publicboolean[] getSelStateList()
{
return selState;
}
// EmptyIcon implementation
publicstaticclass EmptyIconimplements Icon{
int width = 16, height = 16;
public EmptyIcon(){
setSize(16,16);
}
public EmptyIcon(int width,int height){
setSize(width, height);
}
publicvoid setSize(int width,int height){
this.width = width;
this.height = height;
}
publicint getIconWidth(){return width;}
publicint getIconHeight(){return height;}
publicvoid paintIcon(Component c, Graphics g,int x,int y){}
}
}

