/* Copyright (c) 2008 Bluendo S.r.L.
* See about.html for details about license.
*
* $Id: UIPanel.java 1577 2009-06-15 14:38:27Z luca $
*/
package it.yup.ui;
import java.util.Enumeration;
import java.util.Vector;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
/**
* A panel that stacks vertically items vertically. If the container height is
* exceeeded, a scrollbar is drawn.
*/
public class UIPanel extends UIItem implements UIIContainer {
/** the contained items */
private Vector items;
/** real height */
int maxHeight;
/** the first visible item */
private int firstVisible;
/** the last visible item */
protected int lastVisible;
/** the selected item */
protected int selectedIdx;
private boolean listMode = false;
private Image pointer = UICanvas.getUIImage("/icons/menuarrow.png");
private int reverseColor = UIUtils.colorize(UIConfig.bg_color, -10);
/*
* If it is equal to true the panel support "roll up" and "roll down"
*/
private boolean modal = false;
public UIPanel(boolean modal, boolean listMode) {
this();
this.modal = modal;
this.listMode = listMode;
}
public UIPanel() {
selectedIdx = -1;
maxHeight = -1;
items = new Vector();
}
public void setScreen(UIScreen _us) {
screen = _us;
Enumeration en = this.items.elements();
while (en.hasMoreElements()) {
UIItem uit = (UIItem) en.nextElement();
uit.setScreen(_us);
}
}
protected void paint(Graphics g, int w, int h) {
int rh = computeRealHeight(g);
if (rh > h) {
w -= UIConfig.scrollbarWidth; // + 1;
}
int otx = g.getTranslateX();
int oty = g.getTranslateY();
// if selectedIndex is after lastVisible or before firstVisible I could do
// need many redraw
boolean needRedraw = true;
while (needRedraw) {
needRedraw = false;
int th = 0;
// an optimisation extremely useful especially for the UIaccordion
int l = 0;
Enumeration en = this.items.elements();
int size = items.size();
while (en.hasMoreElements() && th < h) {
UIItem ui = (UIItem) en.nextElement();
if (l >= firstVisible && l < size) {
int ih = ui.getHeight(g);
if (ui.isDirty()) {
paintIthItem(g, w, ui, ih,l);
}
g.translate(0, ih);
th += ih;
lastVisible = l;
}
l++;
}
g.translate(otx - g.getTranslateX(), oty - g.getTranslateY());
if (th > h) {
/* the last item is not fully visible */
lastVisible--;
}
if (th < h) {
/* fill the gap */
int oc = g.getColor();
g.setColor(getBg_color() >= 0 ? getBg_color()
: UIConfig.bg_color);
g.fillRect(0, th, w, h - th);// - 1);
g.setColor(oc);
}
/* scroll down -> need to calculate space */
if (selectedIdx != -1 && selectedIdx > lastVisible
&& this.selectedIdx > this.firstVisible && selectedIdx<items.size()) {
int delta = 0;
for (int i = lastVisible + 1; i <= selectedIdx; i++) {
delta += ((UIItem) items.elementAt(i)).getHeight(g);
}
do {
firstVisible++;
delta -= ((UIItem) items.elementAt(firstVisible))
.getHeight(g);
} while (delta > 0 && firstVisible < this.items.size() - 1);
for (int i = firstVisible; i < this.items.size(); i++) {
((UIItem) items.elementAt(i)).setDirty(true);
}
needRedraw = true;
}
/* up check is easier ;) */
if (selectedIdx != -1 && selectedIdx < firstVisible) {
firstVisible = selectedIdx;
for (int i = firstVisible; i < this.items.size(); i++) {
((UIItem) items.elementAt(i)).setDirty(true);
}
needRedraw = true;
}
}
if (rh > h) {
w += UIConfig.scrollbarWidth;// + 1;
drawScrollBar(g, w, h, rh);
}
/* resets origin to old value */
g.translate(otx - g.getTranslateX(), oty - g.getTranslateY());
}
private void paintIthItem(Graphics g, int w, UIItem ui, int ih,int l) {
if (listMode == false) {
ui.paint0(g, w, ih);
return;
}
int oc = g.getColor();
boolean changeColor = (l%2 ==1);
int xOffset = pointer.getWidth() + 1;
if (changeColor) g.setColor(reverseColor);
else
g.setColor(getBg_color() >= 0 ? getBg_color() : UIConfig.bg_color);
g.fillRect(0, 0, xOffset, ih);
if (ui.isSelected()) g.drawImage(pointer, 0,
(ih - pointer.getHeight()) / 2, Graphics.TOP | Graphics.LEFT);
g.setColor(oc);
g.translate(xOffset, 0);
int oldBgColor = ui.getBg_color();
if (changeColor) ui.setBg_color(reverseColor);
ui.paint0(g, w - xOffset, ih);
ui.setBg_color(oldBgColor);
g.translate(-xOffset, 0);
}
/**
* Draws the scrollbar for the given rectangle
*
* @param g
* the graphics context (origin should be translated)
* @param w
* the width of this panel
* @param h
* the height of this panel
* @param rh
* the real height of this panel
*/
protected void drawScrollBar(Graphics g, int w, int h, int rh) {
int otx = g.getTranslateX();
int oty = g.getTranslateY();
int oc = g.getColor();
g.setColor(UIConfig.scrollbar_bg);
g.translate(w - UIConfig.scrollbarWidth, 0);
g.fillRect(0, 0, UIConfig.scrollbarWidth, h);
/* calculate y and height of scrollbar */
int sy = h * firstVisible / items.size();
int sh = (h * h) / rh;
if (sy + sh > h || lastVisible == items.size() - 1) {
sy = h - sh;
}
g.setColor(UIConfig.scrollbar_fg);
g.fillRect(1, sy, UIConfig.scrollbarWidth - 2, sh);
/* resets origin to old value */
g.translate(otx - g.getTranslateX(), oty - g.getTranslateY());
g.setColor(oc);
}
public int getHeight(Graphics g) {
/* always all the available space */
if (maxHeight != -1) { return maxHeight; }
if (this.height > 0) return this.height;
// if i have a clip that is my height
int clipY = g.getClipY();// .getClipY();
int clippedHeight = g.getClipHeight() + clipY;
// if (clippedHeight > 0)
this.height = clippedHeight;
return this.height;
// otherwise my last known height
}
public void setDirty(boolean _dirty) {
// for (int i = 0; i < items.size(); i++) {
// UIItem ui = (UIItem) items.elementAt(i);
// ui.setDirty(_dirty);
// }
Enumeration en = this.items.elements();
while (en.hasMoreElements()) {
UIItem ui = (UIItem) en.nextElement();
ui.setDirty(true);
}
dirty = _dirty;
// so that it will be computed again !
height = -1;
}
public boolean isDirty() {
for (int i = 0; i < this.items.size(); i++)
if (((UIItem) this.items.elementAt(i)).isDirty()) return true;
if (this.dirty) return true;
return false;
}
/**
* Calculates the real height of the item
*/
protected int computeRealHeight(Graphics g) {
int realHeight = 0;
/*
* saves the old coordinate origin, and calculates the height of each
* contained item
*/
int otx = g.getTranslateX();
int oty = g.getTranslateY();
Enumeration en = this.getItems().elements();
while (en.hasMoreElements()) {
int ih = ((UIItem) en.nextElement()).getHeight(g);
g.translate(0, ih);
realHeight += ih;
}
/* resets origin to old value */
g.translate(otx - g.getTranslateX(), oty - g.getTranslateY());
return realHeight;
}
protected void updateChildren() {
int index = 0;
for (Enumeration en = this.items.elements(); en.hasMoreElements();) {
UIItem item = (UIItem) en.nextElement();
if (this.selectedIdx != index) {
item.setSelected(false);
}
index++;
}
((UIItem) this.items.elementAt(selectedIdx)).setSelected(true);
this.setDirty(true);
this.askRepaint();
}
/**
*
*/
public boolean keyPressed(int key) {
if (this.selectedIdx >= 0
&& selectedIdx < this.items.size()
&& ((UIItem) this.items.elementAt(selectedIdx)).keyPressed(key) == true) {
// this is needed since we cannot know if anything below has been
// repainted
updateChildren();
return true;
}
int ga = UICanvas.getInstance().getGameAction(key);
boolean keepFocus = true;
int nsi;
UIItem ui = null;
if (items.size() == 0) { return false; }
int tempIndex = 0;
Enumeration en=null;
switch (ga) {
case Canvas.DOWN:
if (selectedIdx == -1) {
/* XXX: nothing selected still: select first visible */
// selectedIdx = firstVisible - 1;
}
keepFocus = false;
// if none of the following items can be focused
// the UIPanel looses the focus
for (int i = selectedIdx + 1; i < items.size() && items.size()>0; i++) {
if (((UIItem) this.items.elementAt(i)).isFocusable() == true) {
keepFocus = true;
break;
}
}
int lastSelectableIndex = -1;
tempIndex = 0;
en = this.items.elements();
while (en.hasMoreElements()) {
UIItem ithObject = (UIItem) en.nextElement();
if (ithObject.isFocusable())
lastSelectableIndex = tempIndex;
tempIndex++;
}
if (selectedIdx >= lastSelectableIndex) {
/*
* end of list, won't go further: will lose selection if there's
* another item after
*/
if (modal == false) {
keepFocus = false;
break;
} else {
// restart selection from start
ui = (UIItem) items.elementAt(selectedIdx);
ui.setSelected(false);
for (int i =0 ; i < firstVisible;i++){
((UIItem) items.elementAt(i)).setDirty(true);
}
firstVisible=0;
selectedIdx = -1;
}
}
dirty = true;
/* set selection on next item (if exists) */
nsi = selectedIdx + 1;
for (; nsi < items.size(); nsi++) {
ui = (UIItem) items.elementAt(nsi);
if (ui.isFocusable()) {
ui.setSelected(true);
keepFocus = true;
/* breaks out of loop not from switch */
break;
}
}
/*
* found another selectable item and there was another old selected
* item, remove selection from old item
*/
if (selectedIdx != -1 && nsi < items.size()) {
ui = (UIItem) items.elementAt(selectedIdx);
ui.setSelected(false);
}
/* found another selectable item, select it */
if (nsi < items.size()) {
selectedIdx = nsi;
} else if (lastVisible < items.size() - 1) {
/*
* there's still something after but it's not visible, move down
* so it can be shown
*/
firstVisible++;
for (int i = firstVisible; i < items.size(); i++) {
((UIItem) items.elementAt(i)).setDirty(true);
}
}
for (int i = Math.max(selectedIdx, 0); i < items.size(); i++) {
((UIItem) items.elementAt(i)).setDirty(true);
}
break;
case Canvas.UP:
if (selectedIdx == -1) {
/* XXX: nothing selected, select last visible */
// selectedIdx = lastVisible + 1;
}
keepFocus = false;
if (selectedIdx == -1 && modal == true) {
selectedIdx = this.getItems().size();
}
// if none of the previous items can be focused
// the UIPanel looses the focus
for (int i = 0; i < selectedIdx; i++) {
if (((UIItem) this.items.elementAt(i)).isFocusable() == true) {
keepFocus = true;
break;
}
}
int firstSelectableIndex = -1;
tempIndex = 0;
en = this.items.elements();
while (en.hasMoreElements()) {
UIItem ithObject = (UIItem) en.nextElement();
if (ithObject.isFocusable()) {
firstSelectableIndex = tempIndex;
break;
}
tempIndex++;
}
if (selectedIdx >= 0 && selectedIdx == firstSelectableIndex) {
/* start of list, won't go further */
// selectedIdx = -1;
// ui = (UIItem) items.elementAt(0);
// ui.setSelected(false);
if (modal == false) {
keepFocus = false;
break;
} else {
// restart selection from the end
ui = (UIItem) items.elementAt(selectedIdx);
ui.setSelected(false);
selectedIdx = this.items.size();
}
}
dirty = true;
/* set selection on previous item (if exists) */
nsi = selectedIdx - 1;
ui = null;
for (; nsi >= 0; nsi--) {
ui = (UIItem) items.elementAt(nsi);
if (ui.isFocusable()) {
ui.setSelected(true);
keepFocus = true;
/* breaks out of loop not from switch */
break;
}
}
if (nsi >= 0) {
/* found another selectable item, remove selection from old item */
if (selectedIdx < this.items.size()) {
ui = (UIItem) items.elementAt(selectedIdx);
ui.setSelected(false);
}
selectedIdx = nsi;
} else if (firstVisible > 0) {
/* move up anyway */
firstVisible--;
for (int i = firstVisible; i < items.size(); i++) {
((UIItem) items.elementAt(i)).setDirty(true);
}
}
for (int i = Math.max(selectedIdx, 0); i < items.size(); i++) {
((UIItem) items.elementAt(i)).setDirty(true);
}
break;
default:
break;
}
// If I am loosing the focus reset the selection to the first index
if (keepFocus == false && this.getSelectedIndex() >= 0
&& this.getSelectedIndex() < this.getItems().size()) {
((UIItem) this.getItems().elementAt(this.getSelectedIndex()))
.setSelected(false);
this.setSelectedIndex(-1);
dirty=true;
}
if (key == UICanvas.MENU_LEFT || key == UICanvas.MENU_RIGHT) {
keepFocus = false;
}
if (dirty) {
askRepaint();
}
return keepFocus;
}
/**
* For now, always focusable
*/
public boolean isFocusable() {
return true;
}
/**
* Selection status change. Whene select becomes true, select first item if
* no item has been selected. De-select last selected item if Panel gets
* de-selected.
*
* @param _selected
* {@code true} if panel becomes selected, {@code false}
* otherwise.
*/
public void setSelected(boolean _selected) {
super.setSelected(_selected);
if (_selected && selectedIdx == -1 && items.size() > 0) {
// try to select an Item (normally)
UIItem firstItem = ((UIItem) items.elementAt(0));
if (firstItem.isFocusable()) {
selectedIdx = 0;
firstItem.setSelected(_selected);
}
}
if (selectedIdx >= 0 && selectedIdx < items.size()) {
((UIItem) items.elementAt(selectedIdx)).setSelected(_selected);
}
}
/**
* Sets the maximum height for this Panel. A max height of -1 (or any
* negative value) indicates that the Panel should take up all the available
* space.
*
* @param mh
* The new max height
*/
public void setMaxHeight(int mh) {
if (mh < -1) {
mh = -1;
}
maxHeight = mh;
}
/**
* @return The current max height or -1 if this Panel takes up all the
* available space.
*/
public int getMaxHeight() {
return maxHeight;
}
/**
* Adds an item to the panel
*
* @param it
* The item to add
*/
public void addItem(UIItem it) {
items.addElement(it);
it.setScreen(this.screen);
it.setContainer(this);
this.dirty = true;
this.height = -1;
}
/**
*
* The number of items in the Panel.
*
* @return The number of items.
*/
public Vector getItems() {
return this.items;
}
public void setItems(Vector v) {
this.items = v;
}
/**
* Removes an item from the panel
*
* @param it
* The item to remove
*/
public int removeItem(UIItem it) {
int iIndex = items.indexOf(it);
if (this.screen != null) {
this.screen.removePaintedItem(it);
}
this.removeItemAt(iIndex);
return iIndex;
}
/**
* Removes the item at the given index from the list.
*
* @param idx
* The item index to remove
*/
public void removeItemAt(int idx) {
if (idx < 0 || idx > items.size()) { return; }
if (selectedIdx > idx) {
/* clear selection */
selectedIdx -= 1;
}
UIItem ithItem = ((UIItem) items.elementAt(idx));
ithItem.setSelected(false);
items.removeElementAt(idx);
if (this.screen != null) {
this.screen.removePaintedItem(ithItem);
}
if (selectedIdx < 0 || selectedIdx >= this.items.size()) selectedIdx = idx - 1;
int startDirty = (selectedIdx >= 0 ? Math.min(idx, selectedIdx) : idx);
for (int i = startDirty; i < items.size(); i++) {
UIItem ithElem = ((UIItem) items.elementAt(i));
ithElem.setDirty(true);
}
if (idx == selectedIdx) {
for (int i = selectedIdx; i < items.size(); i++) {
UIItem ithElem = ((UIItem) items.elementAt(i));
if (ithElem.isFocusable()) {
this.setSelectedItem(ithElem);
break;
}
}
}
this.dirty = true;
}
/**
* Removes the item at the given index from the list.
*
* @param idx
* The item index to remove
*/
public void insertItemAt(UIItem it, int idx) {
if (idx < 0 || idx > items.size()) { return; }
if (selectedIdx >= idx && selectedIdx < items.size()) {
/* move selection after */
((UIItem) this.getItems().elementAt(selectedIdx))
.setSelected(false);
if (selectedIdx < items.size()) {
selectedIdx++;
}
}
items.insertElementAt(it, idx);
if (selectedIdx >= 0) ((UIItem) this.getItems().elementAt(selectedIdx))
.setSelected(true);
for (int i = idx + 1; i < items.size(); i++) {
((UIItem) items.elementAt(i)).setDirty(true);
}
it.setContainer(this);
it.setScreen(this.screen);
this.dirty = true;
}
/**
* Remove all elements
*/
public void removeAllItems() {
if (selectedIdx > 0 && selectedIdx < this.items.size()) ((UIItem) items
.elementAt(this.selectedIdx)).setSelected(false);
items.removeAllElements();
setDirty(true);
selectedIdx = -1;
firstVisible = 0;
}
/**
* Sets the currently selected item at the position given.
*
* @param idx
* The index to select. -1 to clear selection
*/
public void setSelectedIndex(int idx) {
if (this.getContainer() != null) {
this.getContainer().setSelectedItem(this);
}
if (idx < -1 || idx >= items.size()) {
/* wrong index, ignore */
return;
}
if (selectedIdx != -1) {
UIItem selItem = ((UIItem) items.elementAt(selectedIdx));
selItem.setSelected(false);
selItem.setDirty(true);
}
selectedIdx = idx;
if (selectedIdx != -1) {
UIItem ithItem = ((UIItem) items.elementAt(idx));
ithItem.setSelected(true);
ithItem.setDirty(true);
}
if (selectedIdx > lastVisible) {
// forcing it to be the last visible to avoid useless
// redraw
lastVisible = selectedIdx;
}
}
/**
* Return the selected UIItem within the UIItem itself; usually it is the
* UIItem itself but in the subclasses (like UIVLayout) it could be one of
* the contained object.
*
* @return
*/
public UIItem getSelectedItem() {
if (this.selectedIdx >= 0
&& this.items.size() >= (this.selectedIdx + 1)) {
return ((UIItem) this.items.elementAt(this.selectedIdx))
.getSelectedItem();
} else {
return this;
}
}
public void setSelectedItem(UIItem item) {
int index = this.items.indexOf(item);
this.setSelectedIndex(index);
}
public boolean contains(UIItem item) {
if (this.items.contains(item)) return true;
Enumeration en = this.items.elements();
while (en.hasMoreElements()) {
UIItem ithItem = (UIItem) en.nextElement();
if (ithItem instanceof UIIContainer) {
UIIContainer iic = (UIIContainer) ithItem;
if (iic.contains(item)) return true;
}
}
return false;
}
/**
* @param modal the modal to set
*/
public void setModal(boolean modal) {
this.modal = modal;
}
/**
* @return the modal
*/
public boolean isModal() {
return modal;
}
public int getSelectedIndex() {
return this.selectedIdx;
}
public void setFirstVisible(int firstVisible) {
this.firstVisible = firstVisible;
}
public int getFirstVisible() {
return firstVisible;
}
public void setListMode(boolean listMode) {
this.listMode = listMode;
}
public boolean isListMode() {
return listMode;
}
}