/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores
* CA 94065 USA or visit www.oracle.com if you need additional information or
* have any questions.
*/
package com.sun.lwuit.list;
import com.sun.lwuit.Container;
import com.sun.lwuit.Graphics;
import com.sun.lwuit.Component;
import com.sun.lwuit.Display;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.events.DataChangedListener;
import com.sun.lwuit.events.SelectionListener;
import com.sun.lwuit.geom.Dimension;
import com.sun.lwuit.layouts.Layout;
import com.sun.lwuit.util.EventDispatcher;
import java.util.Vector;
/**
* This is a "list component" implemented as a container with a layout manager
* which provides <b>some</b> of the ui advantages of a Container and some of a
* list while pulling out some of the drawbacks of both.
* This container uses the model/renderer approach for populating itself, adding/removing
* entries will probably break it. It still provides most of the large size advantages
* a list offers since the components within it are very simple and don't contain any
* actual state other than layout information. The big advantage with this class is
* the ability to leverage elaborate LWUIT layouts such as Grid, Table & flow layout
* to provide other ways of rendering the content of a list model.
*
* @author Shai Almog
*/
public class ContainerList extends Container {
private CellRenderer renderer = new DefaultListCellRenderer();
private ListModel model;
private Listeners listener;
private EventDispatcher dispatcher = new EventDispatcher();
/**
* Default constructor
*/
public ContainerList() {
this(new DefaultListModel());
}
/**
* Constructs a container list with the given model
*
* @param m the model
*/
public ContainerList(ListModel m) {
init(m);
}
private void init(ListModel m) {
setModel(m);
setUIID("ContainerList");
setScrollableY(true);
}
/**
* Constructs a container list with the given model and layout
*
* @param l layout manager
* @param m the model
*/
public ContainerList(Layout l, ListModel m) {
super(l);
init(m);
}
/**
* The renderer used to draw the container list elements
*
* @param r renderer instance
*/
public void setRenderer(CellRenderer r) {
renderer = r;
repaint();
}
/**
* The renderer used to draw the container list elements
*/
public CellRenderer getRenderer() {
return renderer;
}
private void updateComponentCount() {
int cc = getComponentCount();
int modelCount = model.getSize();
if(cc != modelCount) {
if(cc < modelCount) {
for(int iter = cc ; iter < modelCount ; iter++) {
addComponent(new Entry(iter));
}
} else {
while(getComponentCount() > modelCount) {
removeComponent(getComponentAt(getComponentCount() - 1));
}
}
}
}
/**
* Returns the list model
*
* @return the list model
*/
public ListModel getModel() {
return model;
}
/**
* Allows binding a listener to user selection actions
*
* @param l the action listener to be added
*/
public void addActionListener(ActionListener l) {
dispatcher.addListener(l);
}
/**
* This method allows extracting the action listeners from the current list
*
* @return vector containing the action listeners on the list
*/
public Vector getActionListeners() {
return dispatcher.getListenerVector();
}
/**
* Allows binding a listener to user selection actions
*
* @param l the action listener to be removed
*/
public void removeActionListener(ActionListener l) {
dispatcher.removeListener(l);
}
/**
* @inheritDoc
*/
protected void initComponent() {
if(model != null) {
int i = model.getSelectedIndex();
if(i > 0) {
getComponentAt(i).requestFocus();
}
}
}
/**
* Set the model for the container list
*
* @param model a model class that is mapped into the internal components
*/
public void setModel(ListModel model) {
if (this.model != null) {
this.model.removeDataChangedListener(listener);
this.model.removeSelectionListener(listener);
listener = null;
}
this.model = model;
updateComponentCount();
if(model.getSelectedIndex() > 0) {
getComponentAt(model.getSelectedIndex()).requestFocus();
}
if (isInitialized()) {
bindListeners();
}
repaint();
}
private void bindListeners() {
if (listener == null) {
listener = new Listeners();
model.addDataChangedListener(listener);
model.addSelectionListener(listener);
}
}
/**
* Returns the current/last selected item
* @return selected item or null
*/
public Object getSelectedItem() {
int i = model.getSelectedIndex();
if(i > -1) {
return model.getItemAt(i);
}
return null;
}
/**
* @inheritDoc
*/
public String[] getPropertyNames() {
return new String[] {"ListItems", "Renderer"};
}
/**
* @inheritDoc
*/
public Class[] getPropertyTypes() {
return new Class[] {Object[].class, CellRenderer.class};
}
/**
* @inheritDoc
*/
public Object getPropertyValue(String name) {
if(name.equals("ListItems")) {
Object[] obj = new Object[model.getSize()];
for(int iter = 0 ; iter < obj.length ; iter++) {
obj[iter] = model.getItemAt(iter);
}
return obj;
}
if(name.equals("Renderer")) {
return getRenderer();
}
return null;
}
/**
* @inheritDoc
*/
public String setPropertyValue(String name, Object value) {
if(name.equals("ListItems")) {
setModel(new DefaultListModel((Object[])value));
return null;
}
if(name.equals("Renderer")) {
setRenderer((CellRenderer)value);
return null;
}
return super.setPropertyValue(name, value);
}
/**
* This class is an internal implementation detail
*/
class Entry extends Component {
private int offset;
Entry(int off) {
setFocusable(true);
offset = off;
}
public void initComponent() {
offset = getParent().getComponentIndex(this);
}
protected void focusGained() {
model.setSelectedIndex(offset);
}
public void paintBackground(Graphics g) {
}
public void paintBorder(Graphics g) {
}
public void paint(Graphics g) {
Component cmp = renderer.getCellRendererComponent(ContainerList.this, model, model.getItemAt(offset), offset, hasFocus());
cmp.setX(getX());
cmp.setY(getY());
cmp.setWidth(getWidth());
cmp.setHeight(getHeight());
cmp.paintComponent(g);
}
/**
* @inheritDoc
*/
public void pointerReleased(int x, int y) {
super.pointerReleased(x, y);
if (!isDragActivated()) {
dispatcher.fireActionEvent(new ActionEvent(ContainerList.this, x, y));
}
}
/**
* @inheritDoc
*/
public void keyReleased(int keyCode) {
super.keyReleased(keyCode);
if(Display.getInstance().getGameAction(keyCode) == Display.GAME_FIRE) {
dispatcher.fireActionEvent(new ActionEvent(ContainerList.this, keyCode));
}
}
public Dimension calcPreferredSize() {
return renderer.getCellRendererComponent(ContainerList.this, model, model.getItemAt(offset), offset, hasFocus()).getPreferredSize();
}
}
private class Listeners implements DataChangedListener, SelectionListener {
public void dataChanged(int status, int index) {
updateComponentCount();
}
public void selectionChanged(int oldSelected, int newSelected) {
getComponentAt(newSelected).requestFocus();
}
}
}