Hello All, it's been a while since I've had a question for you all.
I'm trying to write a paint app, I've done so before, but this time I decided to add Layers, and I wanted a JList of the layers, I wrote a cell renderer, that not only renders the names of the layer, but a small scale image of the layer, but I also have two checkboxes on the component that gets returned, and the checkboxes don't change when clicked, is there a simple way to make the list pass events down to other components?

Recommended Answers

All 3 Replies

Simple way? Not that I know of. JList is not really considered an editing component and doesn't expose methods for it list JTable does. What you need is a cell editor in addition to your renderer.

You could take a look at this guy's blog entry where he builds an editable JList and perhaps modify it to your needs: http://www.jroller.com/santhosh/date/20050607

Or you could use a JTable instead, with a custom cell renderer.

I didn't want to post my code before because it's long and dididn't have any comments, now that I've commented it I suppose I'll post it. I have the JList displaying exactly what I wanted to, including the CheckBoxes, but I can't click the checkboxes. Here is my current code

ListPane.java -- test driver, eventually going to be directly in the paint program

package paintpp.layers;

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;


/**
 * @author Bill
 *
 */
public class LayerPane extends JPanel {
    /**
     * the list that will be displayed
     */
    JList list = new JList();
    /**
     * scroll pane for the list
     */
    JScrollPane pane = new JScrollPane(list);
    /**
     * model for the list
     */
    public DefaultListModel model = new DefaultListModel();
    /**
     * renders a layer in list cell format
     */
    LayerListRenderer rend = new LayerListRenderer();
    /**
     * make a new default layer pane
     */
    public LayerPane() {
        list.setCellRenderer( rend );
        list.setModel( model );
        this.add( pane );
    }

    /**
     * because eclipse complains if I don't make one
     */
    private static final long serialVersionUID = -4271150524981636755L;

    /**
     * main just to test if stuff is working with the list managing classes here
     // FIXME NOT WORKING JList isn't allowing events to reach the checkboxes in the list
     // display works well, though
     * @param args command line arguments
     */
    public static void main( String[] args ) {
        //frame for test
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        //test layerpane
        LayerPane lpane = new LayerPane();
        frame.add(lpane);
        //add three new layers to the list, and for each of them generate random colors throughout
        lpane.model.add(0, new Layer("hello",100,100) );
        RandomImage((Layer)lpane.model.get( 0 ));
        lpane.model.add(0, new Layer("second",100,100) );
        RandomImage((Layer)lpane.model.get( 0 ));
        lpane.model.add(0, new Layer("third",100,100) );
        RandomImage((Layer)lpane.model.get( 0 ));
        //set visible
        frame.setVisible( true );
    }
    /**
     * random colors
     * @param l graphics
     */
    public static void RandomImage(Layer l) {
        Graphics g = l.getGraphics();
        for(int i = 0; i<l.getWidth();i++) {
            for(int j = 0; j<l.getHeight();j++) {
                g.setColor( new Color((int)Math.floor( Math.random()*256 ),(int)Math.floor( Math.random()*256 ),(int)Math.floor( Math.random()*256 )) );
                g.drawRect(i,j,0,0);
            }
        }
    }
}

Layer.java -- an extended BufferredImage, that now has a name and a few extra flags

/**
 * 
 */
package paintpp.layers;

import java.awt.image.BufferedImage;


/**
 * A class to hold a Layer, Comparable, only so that treemap will work correctly
 * @author Bill
 *
 */
public class Layer extends BufferedImage implements Comparable<Layer>{
    /**
     * name of this layer
     */
    String name;
    /**
     * is the layer visible
     */
    private boolean visible = true;
    /**
     * is the layer locked (not moddable)
     */
    private boolean locked = false;
    
    /**
     * Constructor
     * @param name name of the layer
     * @param width width of the layer
     * @param height height of the layer
     */
    public Layer(String name, int width, int height) {
        this(name,width,height,TYPE_INT_ARGB);
    }
    /**
     * Constructor
     * @param name name of the layer
     * @param width width of the layer
     * @param height height of the layer
     * @param imageType type of image
     */
    public Layer(String name, int width, int height, int imageType ) {
        super( width, height, imageType );
        this.name=name;
    }
    
    /**
     * getter for name
     * @return name
     */
    public String getName() {
        return name;
    }
    /**
     * @param visible the visible to set
     */
    public void setVisible( boolean visible ) {
        this.visible = visible;
    }
    /**
     * @return the visible
     */
    public boolean isVisible() {
        return visible;
    }
    /**
     * setter for name
     * @param nName new name
     */
    public void setName(String nName) {
        name=nName;
    }
    /**
     * @param locked the locked to set
     */
    public void setLocked( boolean locked ) {
        this.locked = locked;
    }
    /**
     * @return the locked
     */
    public boolean isLocked() {
        return locked;
    }
    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals( Object arg0 ) {
        if(arg0 instanceof Layer) {
            return super.equals( arg0 )&&((Layer)arg0).name.equals(name);
        }
        return false;
    }
    /**
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        return super.hashCode()+2*name.hashCode();
    }
    /**
     * 
     */
    @Override
    public int compareTo( Layer o ) {
        return name.compareTo( o.name );
    }
    
}

LayerListRenderer.java -- the list renderer

package paintpp.layers;

import java.awt.Component;

import javax.swing.JList;
import javax.swing.ListCellRenderer;

import java.util.TreeMap;

/**

 * To render layers in a list, it's a simple thing turned complex by me<br>
 * rather than just return a new component every time the only required<br>
 * method is called I use a Treemap, so that I don't have to construct a<br>
 * new thing every time, and I was hoping that because the components are<br>
 * really there, that the List might pass events to them and I don't know why not.
 * FIXME really need the checkboxes to get events to them
 * @author Bill
 *
 */
public class LayerListRenderer implements ListCellRenderer {
    /**
     * treemap that stores the layer panels accordign to the layers that they can display
     */
    TreeMap<Layer, LayerPanel> renderers = new TreeMap<Layer,LayerPanel>();

    /**
     * because eclipse made me
     */
    private static final long serialVersionUID = -6121867104182335719L;

    /**
     * fairly simple, if the map has the specified layer, return the component that is already there to display it,<br>
     * if not make a new one, add it to the map, and return it;
     * @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean)
     */
    @Override
    public Component getListCellRendererComponent( JList list, Object value,
            int index, boolean isSelected, boolean cellHasFocus ) {
        if(value instanceof Layer) {
            Layer l = (Layer)value;
            if(renderers.containsKey( l )) {
                return renderers.get( l ).setSelected( isSelected );
            } else {
                renderers.put( l, new LayerPanel(l) );
                return renderers.get( l ).setSelected( isSelected );
            }
        }
        return null;
    }
}

LayerPanel.java -- the component that actually knows how to display a layer

/**
 * 
 */
package paintpp.layers;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.JCheckBox;
import javax.swing.JPanel;


/**
 * Class that displays a layer for a list of layers.
 * also contains checkboxes that should control what functionality the layer currently supports.
 * FIXME the checkboxes aren't responding to events.
 * @author Bill
 */
public class LayerPanel extends JPanel implements ItemListener {
    
    /**
     * Layer to display
     */
    Layer layer;
    
    /**
     * whether this is a selected component
     */
    boolean isSelected;
    
    /**
     * checkbox for visibility
     */
    JCheckBox visible = new JCheckBox("Visible",true);
    
    /**
     * checkbox for locking
     */
    JCheckBox locked = new JCheckBox("Locked",false);
    
    /**
     * new renderer with layer nl
     * @param nl layer to display with this
     */
    public LayerPanel(Layer nl) {
        layer=nl;
        visible.addItemListener( this );
        locked.addItemListener( this );
        this.add( new Preview() );
        this.add( visible );
        this.add( locked );
        this.setPreferredSize( new Dimension(200,50) );
    }
    
    /**
     * sets the selected value, and returns the newly changed this
     * @param bool new value
     * @return changed this
     */
    public LayerPanel setSelected(boolean bool) {
        isSelected=bool;
        if(isSelected) {
            this.setBackground( new Color(100,100,255) );
        } else {
            this.setBackground( new Color(255,255,255) );
        }
        return this;
    }
    
    /**
     * this is what will notify the underlying layer that the checkboxes are changed.
     */
    @Override
    public void itemStateChanged( ItemEvent e ) {
        if(e.getSource()==visible) {
            if(e.getStateChange()==ItemEvent.SELECTED) {
                layer.setVisible( true );
            } else {
                layer.setVisible( false );
            }
        } else if(e.getSource()==locked) {
            if(e.getStateChange()==ItemEvent.SELECTED) {
                layer.setLocked( true );
            } else {
                layer.setLocked( false );
            }
        }
    }
    
    /**
     * this inner class is here to paint the smaller scale image of the layer.
     * all it does is override paint to display the layer it works fine
     * @author Bill
     *
     */
    class Preview extends JPanel {
        private static final long serialVersionUID = 8953781420286542857L;
        public Preview() {
            this.setPreferredSize(new Dimension(20,20));
        }
        public void paint(Graphics g) {
            g.drawImage(layer,0,0,20,20,0,0,layer.getWidth(),layer.getHeight(),null);
        }
    }
    
    /**
     * 
     */
    private static final long serialVersionUID = 8953781420286542857L;


}

I just want the checkboxes to respond to normal clicking, I will take a look at the blog later (I don't really have time at the moment), I suppose I could try to change his text boxes to check boxes, but I would rather not have to radically change what I have.

I hope seeing my code will help you, find something that I can do,

The point is, you have a custom renderer - which is not an editor.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.