/*********************************************************************************
* TotalCross Software Development Kit *
* Copyright (C) 2009-2012 SuperWaba Ltda. *
* All Rights Reserved *
* *
* This library and virtual machine 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. *
* *
* This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 *
* A copy of this license is located in file license.txt at the root of this *
* SDK or can be downloaded here: *
* http://www.gnu.org/licenses/lgpl-3.0.txt *
* *
*********************************************************************************/
package totalcross.ui;
import totalcross.sys.Settings;
import totalcross.sys.SpecialKeys;
import totalcross.ui.event.*;
import totalcross.ui.font.Font;
import totalcross.ui.gfx.Color;
import totalcross.ui.gfx.Graphics;
import totalcross.ui.image.Image;
import totalcross.ui.image.ImageException;
import totalcross.util.Vector;
/**
* ListContainer is a ListBox where each item is a Container.
* <p>
* The correct way to create a ListContainer item is by subclassing a
* Container and adding the controls in the initUI method. Adding
* directly using <code>getContainer(i).add</code> will not work. Below is
* an example of how to use it, taken from the UIGadgets sample.
* <pre>
class LCItem extends ScrollContainer
{
Label lDate,lPrice,lDesc;
Check chPaid;
public LCItem()
{
super(false); // VERY IMPORTANT (a RuntimeException will be thrown if this is not used).
}
public void initUI()
{
add(chPaid = new Check("Paid"),LEFT,TOP);
add(lDate = new Label("99/99/9999"),RIGHT,TOP);
add(new Label("US$"),LEFT,AFTER);
add(lPrice = new Label("999.999.99"),AFTER,SAME);
add(lDesc = new Label("",RIGHT),AFTER+10,SAME);
lDesc.setText("description");
}
}
private void testListContainer()
{
ListContainer lc;
add(lc = new ListContainer(),LEFT,TOP,FILL,FILL);
for (int i =0; i < 10; i++)
lc.addContainer(new LCItem());
}
* </pre>
* When an item is selected, a PRESSED event is dispatched.
*
* Check the ListContainerSample in the sdk for a bunch of ideas of what can be done with this component.
*
* The ListContainer supports navigation using the keys up/down, page-up/down, and enter. The left and right
* keys acts like clicking in the left or right buttons (if any).
*
* @since TotalCross 1.14
*/
public class ListContainer extends ScrollContainer
{
/** A set of fields and default fields that will be used to define the layout of a ListContainer's Item.
*/
public class Layout
{
/** Specify what to do if the left or right controls are images.
* There are two situations that occurs when the image has a different height of the ListContainer's Item:
* <ol>
* <li> The image is smaller than the ListContainer's Item height. It can be enlarged or vertically centered.
* If the flag is false (default), the image will be centered. Otherwise, a "smooth upscaled" image will
* be created, however the image will mostly have a bad appearance. This is the worst choice. The best
* choice is always to have a big image (for the biggest possible resolution of the device) that will
* always be scaled down.
* <li> The image is bigger than the ListContainer's Item height. It will always be scaled down, using a
* "smooth scale" algorithm. This is the best choice.
* </ol>
* In general way, the image never defines the height of the Item; its the opposite: the number of Item
* lines is that defines the image height and size.
*/
public boolean leftImageEnlargeIfSmaller, rightImageEnlargeIfSmaller;
/** If the left and/or right control is a fixed Image, set it here and it will be replicated on all lines. */
public Image defaultLeftImage, defaultRightImage;
protected int defaultLeftImageW,defaultLeftImageH,defaultRightImageW,defaultRightImageH;
/** If the left and/or right control is a fixed Image, set it here and it will be replicated on all lines.
* These images can be set only if the default image was set. The image size must be the same of the default one. */
public Image defaultLeftImage2, defaultRightImage2;
/** The default colors of all items. Defaults to BLACK. */
public int[] defaultItemColors;
/** The items that will have a bold font. Defaults to false (plain font) */
public boolean[] boldItems;
/** The default relative font sizes. You can specify a delta compared to the font size of this ListContainer.
* For example, specifying -1 will make the item have a font 1 size less than the standard one. */
public int[] relativeFontSizes;
/** The x position of the label, relative to the column's width.
* Can be AFTER (default for all items), CENTER (relative to container's width), CENTER_OF (relative to the space
* available after the last String added), RIGHT (adjustments are NOT allowed!), BEFORE.
* The number of lines of the Item is computed based on the column count.
* Note that this field cannot be changed after the first Item is created, since the internal computation of
* number of lines is done only once.
*/
public int[] positions;
/** The gap between the left/right controls and the text.
* This gap is a <b>percentage</b> based in the control font's height. So, if you pass 100 (default), it will be
* 100% of the font's height, 50 will be 50% of the height, 150 will be 1.5x the font height, and so on.
*/
public int controlGap = 100;
/** The gap between the image and the item's top/bottom. This is useful if your item has 3 lines but you want
* to decrase the image's height.
*/
public int imageGap;
/**
* The gap between the Item and the borders can be set using this field. The values stored
* are not absolute pixels, but a percentage. Defaults to 0 (%) for all items.
*/
public Insets insets = new Insets(0,0,0,0);
/** The line spacing between two consecutive lines. Again, a percentage of the font's height is used.
* Defaults to 0.
* @see #controlGap
*/
public int lineGap;
/** Set to true to center the labels vertically if there are empty lines ("") before or after the items array.
* Only works if the items form a single column.
*/
public boolean centerVertically;
protected int itemCount,itemsPerLine;
protected int[] itemY;
protected Font[] fonts;
protected int itemH;
protected ListContainerEvent lce = new ListContainerEvent();
/** Constructs a Layout component with the given columns and item count. */
public Layout(int itemCount, int itemsPerLine)
{
this.itemCount = itemCount;
this.itemsPerLine = itemsPerLine;
defaultItemColors = new int[itemCount];
relativeFontSizes = new int[itemCount];
boldItems = new boolean[itemCount];
fonts = new Font[itemCount];
positions = new int[itemCount];
for (int i = itemCount; --i >= 0;) positions[i] = AFTER;
}
/** After you set the properties, you must call this method to setup the internal coordinates.
* If you create a Item before calling this method, a RuntimeException will be thrown.
*/
public void setup()
{
// compute the number of lines
int lineCount = itemCount / itemsPerLine;
if ((itemCount % itemsPerLine) != 0)
lineCount++;
itemY = new int[itemCount];
itemH=0;
int y = insets.top*fmH/100;
// for each line, compute its height based on the biggest font height
for (int i = 0, lineH = 0, col = 0, last = itemCount-1; i <= last; i++)
{
Font f = fonts[i] = Font.getFont(font.name, boldItems[i], font.size+relativeFontSizes[i]);
itemY[i] = y;
if (f.fm.height > lineH)
lineH = f.fm.height;
if (++col == itemsPerLine || i == last)
{
// adjust the y so that different font heights at the same line are bottom-aligned
for (int j = i; j >= 0 && itemY[j] == y; j--)
itemY[j] += lineH-fonts[i].fm.height;
itemH += lineH;
y += lineH + lineGap*fmH/100;
lineH = col = 0;
}
}
itemH += (lineGap*fmH/100) * (lineCount-1);
// if there are images, resize them accordingly
if (defaultLeftImage != null)
{
defaultLeftImage = resizeImage(defaultLeftImage, leftImageEnlargeIfSmaller);
defaultLeftImageW = defaultLeftImage.getWidth();
defaultLeftImageH = defaultLeftImage.getHeight();
}
if (defaultRightImage != null)
{
defaultRightImage = resizeImage(defaultRightImage, rightImageEnlargeIfSmaller);
defaultRightImageW = defaultRightImage.getWidth();
defaultRightImageH = defaultRightImage.getHeight();
}
if (defaultLeftImage2 != null)
defaultLeftImage2 = resizeImage(defaultLeftImage2, leftImageEnlargeIfSmaller);
if (defaultRightImage2 != null)
defaultRightImage2 = resizeImage(defaultRightImage2, rightImageEnlargeIfSmaller);
}
private Image resizeImage(Image img, boolean imageEnlargeIfSmaller)
{
int imgH = itemH - 2*imageGap*fmH/100;
int ih = img.getHeight();
if (ih > imgH || (imageEnlargeIfSmaller && ih < imgH)) // if the image's height is bigger than the total height, always decrease the size.
try
{
int iw = img.getWidth();
return img.getSmoothScaledInstance(iw * imgH / ih, imgH);
}
catch (ImageException ime) {} // just keep the previous image intact
return img;
}
}
/** Creates a Layout object with the given parameters. */
public Layout getLayout(int itemCount, int itemsPerLine)
{
return new Layout(itemCount, itemsPerLine);
}
/** An item of the ListContainer. */
public static class Item extends Container
{
/** The left and/or right controls that will be displayed. */
public Object leftControl, rightControl;
/** The Strings that will be displayed in the container. Individual items cannot be null;
* pass "" instead to not display it.
*/
public String[] items;
/** The colors of all items. */
private int[] itemColors;
/** When leftControl or rightControl is an Image, set this to false to don't show it (and also disable controls) */
public boolean leftControlVisible=true,rightControlVisible=true;
private Layout layout;
/** Constructs an Item based in the given layout. You must set the items array with the strings that will
* be displayed. You may also set the leftControl/rightControl and individual itemColors and boldItems.
*/
public Item(Layout layout)
{
if (layout.itemY == null)
throw new RuntimeException("You must call layout.setup after setting its properties and before creating an Item. Read the javadocs!");
this.layout = layout;
this.itemColors = layout.defaultItemColors;
if (layout.defaultLeftImage != null)
leftControl = layout.defaultLeftImage;
if (layout.defaultRightImage != null)
rightControl = layout.defaultRightImage;
}
/** Returns the colors used on this Item. You can then set the individual colors if you wish.
* @see ListContainer.Layout#defaultItemColors
*/
public int[] getColors()
{
if (itemColors == layout.defaultItemColors) // still the default? create a new array so user can change
{
itemColors = new int[layout.itemCount];
for (int i = layout.itemCount; --i >= 0;)
itemColors[i] = layout.defaultItemColors[i];
}
return itemColors;
}
public void initUI()
{
super.initUI();
if (items.length != layout.itemCount)
throw new IllegalArgumentException("Items have "+items.length+" but itemCount was specified as "+layout.itemCount+" in the Layout's constructor");
if (leftControl != null && leftControl instanceof Control)
{
Control c = (Control)leftControl;
if (c.parent == null) add(c);
c.setRect(LEFT+layout.insets.left*fmH/100,CENTER,PREFERRED,PREFERRED);
c.focusTraversable = true;
}
if (rightControl != null && rightControl instanceof Control)
{
Control c = (Control)rightControl;
if (c.parent == null) add(c);
c.setRect(RIGHT-layout.insets.right*fmH/100,CENTER,PREFERRED,PREFERRED);
c.focusTraversable = true;
}
}
public void onEvent(Event e)
{
if (e.type == PenEvent.PEN_DOWN || e.type == PenEvent.PEN_UP)
{
if (!isActionEvent(e))
return;
if (leftControl != null && leftControlVisible && (e.target == leftControl || ((PenEvent)e).x < getLeftControlX()))
handleButtonClick(e.target,true);
else
if (rightControl != null && rightControlVisible && (e.target == rightControl || ((PenEvent)e).x >= getRightControlX()))
handleButtonClick(e.target,false);
else
return;
e.consumed = true;
}
}
private void handleButtonClick(Object target, boolean isLeft)
{
boolean is2 = false;
Object c = isLeft ? leftControl : rightControl;
// change images
Image cur = c instanceof Image ? (Image)c : c instanceof Button ? ((Button)c).getImage() : null;
if (cur != null)
{
Image img1 = isLeft ? layout.defaultLeftImage : layout.defaultRightImage;
Image img2 = isLeft ? layout.defaultLeftImage2 : layout.defaultRightImage2;
if (img1 != null && img2 != null) // if there are both images, swap them
{
cur = cur == img1 ? img2 : img1;
is2 = cur == img2;
if (c instanceof Image)
{
if (isLeft)
leftControl = cur;
else
rightControl = cur;
}
else
((Button)c).setImage(cur);
}
}
// flsobral: the target of the pen down event may be the left control instead of an item from this list.
if (!(target instanceof Item)) // this if is just an optimization to avoid starting an unnecessary loop.
{
while (!(target instanceof Item) && target instanceof Control)
target = ((Control) target).parent;
}
if (target instanceof Item) // although unlikely, it is possible for the loop above to set target to an unknown type, this is a last check to avoid a ClassCastException
{
Window.needsPaint = true;
// change to the selected line so user can correctly find who was pressed
ListContainer lc = getLC();
if (lc != null)
lc.setSelectedItem((Container)target);
postListContainerEvent(c, target, isLeft ? ListContainerEvent.LEFT_IMAGE_CLICKED_EVENT : ListContainerEvent.RIGHT_IMAGE_CLICKED_EVENT, is2);
}
}
private ListContainer getLC()
{
for (Control c = parent; c != null; c = c.parent)
if (c instanceof ListContainer)
return (ListContainer)c;
return null;
}
public void setImage(boolean isLeft, boolean toImage1)
{
Object c = isLeft ? leftControl : rightControl;
// change images
if (c instanceof Image)
{
Image img1 = isLeft ? layout.defaultLeftImage : layout.defaultRightImage;
Image img2 = isLeft ? layout.defaultLeftImage2 : layout.defaultRightImage2;
if (img1 != null && img2 != null)
{
if (isLeft)
leftControl = toImage1 ? img1 : img2;
else
rightControl = toImage1 ? img1 : img2;
}
Window.needsPaint = true;
}
else
if (c instanceof Button)
{
Button b = (Button)c;
Image img1 = isLeft ? layout.defaultLeftImage : layout.defaultRightImage;
Image img2 = isLeft ? layout.defaultLeftImage2 : layout.defaultRightImage2;
if (img1 != null && img2 != null)
b.setImage(toImage1 ? img1 : img2);
}
}
public void postListContainerEvent(Object target, Object source, int type, boolean is2)
{
layout.lce.touch();
layout.lce.consumed = false;
layout.lce.target = target;
layout.lce.type = type;
layout.lce.isImage2 = is2;
layout.lce.source = (Control)source;
postEvent(layout.lce);
}
public int getPreferredWidth()
{
return parent.getClientRect().width;
}
public int getPreferredHeight()
{
return layout.itemH + (layout.insets.top+layout.insets.bottom)*fmH/100;
}
public int getLeftControlX()
{
int x1 = layout.insets.left*fmH/100;
if (leftControl != null)
x1 += (leftControl instanceof Control ? ((Control)leftControl).getWidth() : layout.defaultLeftImageW) + layout.controlGap*fmH/100;
return x1;
}
public int getRightControlX()
{
int x2 = width - layout.insets.right*fmH/100;
if (rightControl != null)
x2 -= (rightControl instanceof Control ? ((Control)rightControl).getWidth() : layout.defaultRightImageW) + layout.controlGap*fmH/100;
return x2;
}
public void onPaint(Graphics g)
{
super.onPaint(g);
Layout layout = this.layout;
// compute the area available for the text, excluding the left/right controls
int x1 = getLeftControlX();
int x2 = getRightControlX();
if (leftControl != null && leftControl instanceof Image && leftControlVisible)
g.drawImage((Image)leftControl, layout.insets.left*fmH/100, (height-layout.defaultLeftImageH)/2);
if (rightControl != null && rightControl instanceof Image && rightControlVisible)
g.drawImage((Image)rightControl, width-layout.defaultRightImageW-layout.insets.right*fmH/100, (height-layout.defaultRightImageH)/2);
g.setClip(x1,0,x2-x1,height);
int lastX = 0;
int deltaY = 0;
if (layout.centerVertically)
{
int first = 0, last = 0, firstH=0, lastH = 0;
for (int i = 0; i < layout.itemCount; i++)
if (items[i].equals(""))
{
firstH += layout.fonts[i].fm.height;
first++;
}
else
break;
for (int i = layout.itemCount; --i >= first;)
if (items[i].equals(""))
{
lastH += layout.fonts[i].fm.height;
last++;
}
else
break;
if (first != last)
deltaY += (firstH+lastH+(first-1+last-1)*layout.lineGap*fmH/100)/2;
}
for (int i = 0, col = 0, x = x1; i < layout.itemCount; i++)
{
Font f = layout.fonts[i];
g.setFont(f);
g.foreColor = itemColors[i];
String s = items[i];
int sw = f.fm.stringWidth(s);
int sx;
int sy = layout.itemY[i];
g.backColor = backColor;
switch (layout.positions[i])
{
default:
case AFTER : sx = x; break;
case RIGHT : sx = x2 - sw; if (x > sx) g.fillRect(sx,sy,sw,sy+f.fm.height); break; // erase area only if last text is beyond our limits
case CENTER_OF: sx = x + (x2-x-sw)/2; sw += sx - x; break;
case CENTER: sx = (x2-x1-sw)/2; sw += sx - x; break;
case BEFORE: sx = lastX - sw; break;
}
g.drawText(s, sx, sy + deltaY);
lastX = sx;
x += sw;
if (++col == layout.itemsPerLine)
{
col = 0;
x = x1;
}
}
}
}
private Vector vc = new Vector(20);
protected Container lastSel; //flsobral@tc126_65: ListContainer is no longer restricted to accept only subclasses of ScrollContainer, any subclass of Container may be added to a ListContainer.
protected int lastSelBack,lastSelIndex=-1;
/** Color used to highlight a container. Based on the background color. */
public int highlightColor=-1;
/** If true (default), draws a horizontal line between each container. */
public boolean drawHLine = true;
private Insets scInsets;
private int defaultHighlightColor;
public ListContainer()
{
super(false,true);
focusTraversable = bag.verticalOnly = true;
}
public void onColorsChanged(boolean colorsChanged)
{
super.onColorsChanged(colorsChanged);
defaultHighlightColor = Color.getCursorColor(backColor);
}
/** Returns the number of items of this list */
public int size() // guich@tc126_53
{
return vc.size();
}
/** Adds a new Container to this list.
* @see #addContainers
*/
public void addContainer(Container c)
{
boolean isSC = c instanceof ScrollContainer;
if (isSC)
{
ScrollContainer sc = (ScrollContainer) c;
if (sc.sbH != null || sc.sbV != null)
throw new RuntimeException("The given ScrollContainer "+c+" must have both ScrollBars disabled.");
sc.shrink2size = true;
}
if (drawHLine && !(c instanceof Item)) // make sure that the container has a gap for the border
{
if (scInsets == null) scInsets = new Insets();
c.getInsets(scInsets);
if (scInsets.top == 0)
c.setInsets(scInsets.left, scInsets.right, 1, scInsets.bottom);
}
c.setFocusTraversable(true);
c.containerId = vc.size()+1;
vc.addElement(c);
add(c,LEFT,AFTER,FILL,PREFERRED);
if (isSC)
c.resize();
int n = tabOrder.size();
if (n >= 2 && ((Control)tabOrder.items[n-1]).y < ((Control)tabOrder.items[n-2]).y)
bag.verticalOnly = false;
if (drawHLine && c.borderStyle == BORDER_NONE)
c.borderStyle = BORDER_TOP;
resize();
}
/** Adds an array of Containers to this list. Adding hundreds of containers is hundred times faster using
* this method instead of adding one at a time.
*
* Consider also to increase the value of
* Flick.defaultLongestFlick BEFORE creating the ListContainer, otherwise the user may take forever
* to flick. You can set it to 3 * all.length, if all.length is above 1000.
* @see Flick#defaultLongestFlick
*/
public void addContainers(Container[] all)
{
changed = true;
if (all.length > 0)
{
int vcs = vc.size();
vc.addElements(all);
boolean isSC = all[0] instanceof ScrollContainer; // assume that if one is a ScrollContainer, all are.
boolean isItem = all[0] instanceof Item;
int s = bag.tabOrder.size();
bag.tabOrder.setSize(s+all.length); // increase taborder's size to the final one
if (!isItem)
bag.tabOrder.setSize(s); // Item is added directly
for (int i =0; i < all.length; i++)
{
Container c = all[i];
c.setFocusTraversable(true);
if (isSC)
{
ScrollContainer sc = (ScrollContainer) c;
if (sc.sbH != null || sc.sbV != null)
throw new RuntimeException("The given ScrollContainer "+c+" must have both ScrollBars disabled.");
sc.shrink2size = true;
}
c.containerId = ++vcs;
if (!isItem)
bag.add(c);
else
{
bag.addToList(c);
if (c.foreColor == -1) c.foreColor = this.foreColor;
if (c.backColor == -1) c.backColor = this.backColor;
if (c.font == null)
{
c.font = this.font;
c.fm = font.fm;
c.fmH = fm.height;
}
bag.tabOrder.items[s++] = c;
}
c.x = LEFT; c.y = AFTER; c.width = FILL; c.height = PREFERRED; // positions will be set later on resize
if (isSC)
c.resize();
if (drawHLine && c.borderStyle == BORDER_NONE)
c.borderStyle = BORDER_TOP;
}
}
resize(); // all lines except this one take 10% of the method's time, and this one takes 90%
}
/** Removes all containers of this ListContainer.
* Note that onRemove is not called in the containers.
*/
public void removeAllContainers()
{
if (!vc.isEmpty())
{
bag.verticalOnly = true;
setSelectedIndex(-1);
scrollToControl(bag.children); // reset scrollbars and scroll position
bag.numChildren = 0; bag.tail = bag.children = null; // faster removeAll()
// reset relative-positioning values
bag.lastX=-999999;
bag.lastY=bag.lastW=bag.lastH = 0;
bag.height = getClientRect().height;
if (sbV != null)
sbV.setMaximum(0);
vc.removeAllElements();
bag.tabOrder.removeAllElements();
Window.needsPaint = true;
}
}
private boolean dragged;
public void onEvent(Event e)
{
super.onEvent(e);
switch (e.type)
{
case DragEvent.PEN_DRAG_START:
if (Settings.fingerTouch)
dragged = true;
break;
case PenEvent.PEN_UP:
//case ControlEvent.FOCUS_IN: - will not work now that it can use keys to traverse
if (!(e.target instanceof Ruler))
{
if (dragged) // don't select if user decided to drag the item
{
dragged = false;
return;
}
// find the container that was added to this ListContainer
Control c = (Control)e.target;
while (c != null)
{
if (c.asContainer != null && c.asContainer.containerId != 0)
{
if (c == lastSel)
return;
setSelectedItem((Container)c);
if (c instanceof Item)
((Item)c).postListContainerEvent(this,e.target,ListContainerEvent.ITEM_SELECTED_EVENT,false);
else
postPressedEvent();
break;
}
c = c.parent;
}
}
break;
case KeyEvent.SPECIAL_KEY_PRESS:
if (vc.size() > 1)
{
Item sel = lastSelIndex != -1 && vc.items[lastSelIndex] instanceof Item ? (Item)vc.items[lastSelIndex] : null;
KeyEvent ke = (KeyEvent)e;
if (sel != null && ke.isActionKey())
sel.postListContainerEvent(sel,e.target,ListContainerEvent.ITEM_SELECTED_EVENT,false);
else
switch (ke.key)
{
case SpecialKeys.UP : scroll(-1); break;
case SpecialKeys.DOWN : scroll( 1); break;
case SpecialKeys.PAGE_UP : scroll(-height/((Container)vc.items[0]).height); break;
case SpecialKeys.PAGE_DOWN: scroll(height/((Container)vc.items[0]).height); break;
case SpecialKeys.LEFT:
case SpecialKeys.RIGHT:
if (sel != null)
sel.handleButtonClick(e.target,ke.key == SpecialKeys.LEFT);
break;
}
}
break;
}
}
private void scroll(int v)
{
int idx = lastSelIndex + v;
if (idx < 0) idx = 0;
else
if (idx >= vc.size())
idx = vc.size()-1;
if (idx != lastSelIndex)
setSelectedIndex(idx);
}
/** Returns the selected container, or null if none is selected. */
public Container getSelectedItem()
{
return lastSel;
}
/** Returns the selected index, or -1 if none is selected. */
public int getSelectedIndex()
{
return lastSelIndex;
}
/** Sets the selected container based on its index.
* @param idx The index or -1 to unselect all containers.
*/
public void setSelectedIndex(int idx)
{
if (idx < 0)
{
if (lastSel != null)
setBackColor(lastSel,lastSelBack);
lastSel = null;
lastSelIndex = -1;
}
else
setSelectedItem((Container)vc.items[idx]);
}
/** Sets the selected container. */
public void setSelectedItem(Container c)
{
if (lastSel != null)
setBackColor(lastSel,lastSelBack);
lastSelBack = c.backColor;
lastSel = c;
int hc = highlightColor != -1 ? highlightColor : defaultHighlightColor;
setBackColor(lastSel, hc);
c.setBackColor(hc); //flsobral@tc126_70: highlight the whole selected container
Window.needsPaint = true;
lastSelIndex = c.containerId == 0 ? -1 : c.containerId-1;
// guich@tc150: make sure the item is visible
int yy = c.y + c.parent.y;
if (yy < 0)
scrollContent(0,yy-height+c.height, true);
else
if ((yy+c.height) > height)
scrollContent(0,yy, true);
}
/** Returns the given container number or null if its invalid. */
public Container getContainer(int idx)
{
return idx >= vc.size() ? null : (Container)vc.items[idx];
}
/** Changes the color of all controls inside the given container that matches the background color
* of this ListContainer.
*/
public void setBackColor(Container c, int back)
{
int hc = highlightColor != -1 ? highlightColor : defaultHighlightColor;
c.setBackColor(back);
for (Control child = c.children; child != null; child = child.next)
{
if (child.asContainer == null && (child.backColor == lastSelBack || child.backColor == hc)) // only change color if same background of container's - guich@tc130: don't do this on Containers, since they will be set when calling setBackColor 2 lines below
child.setBackColor(back);
if (child.asContainer != null)
setBackColor(child.asContainer, back);
}
}
public void resize()
{
boolean isItem = bag.children != null && bag.children instanceof Item;
for (Control child = bag.children; child != null; child = child.next)
if (!(child instanceof ScrollContainer))
{
child.setRect(KEEP, KEEP, FILL, KEEP, null, true);
if (child.asContainer != null && child.asContainer.numChildren > 0)
{
Control[] children2 = ((Container) child).getChildren();
if (children2 != null)
for (int j = children2.length; --j >= 0;)
children2[j].reposition();
}
}
if (isItem) // if Item, we know that all of them have the same width and height
{
Control last = bag.tail;
int maxX = last.x+last.width;
int maxY = last.y+last.height;
super.resize(maxX, maxY);
}
else
super.resize();
}
/** Positions the given Container (that should be a control added to this ListContainer) at the top
* of the list.
*/
public void scrollToControl(Control c) // kmeehl@tc100
{
if (c != null && sbV != null)
{
int yy = c.y;
Control f = c.parent;
while (f.parent != this)
{
yy += f.y;
f = f.parent;
if (f == null)
return;// either c is not in this container, or it has since been removed from the UI
}
// vertical
int lastV = sbV.value;
int val = yy - c.parent.y;
if (val < sbV.minimum)
val = sbV.minimum;
else
if (val >= sbV.maximum)
val = sbV.maximum;
sbV.setValue(val);
if (lastV != sbV.value)
{
lastV = sbV.value;
bag.uiAdjustmentsBasedOnFontHeightIsSupported = false;
bag.setRect(bag.x,TOP-lastV,bag.width,bag.height);
bag.uiAdjustmentsBasedOnFontHeightIsSupported = true;
}
}
}
}