/*******************************************************************************
* SuperWaba Virtual Machine, version 4 *
* Copyright (C) 2003-2004 Guilherme Campos Hazan <support@superwaba.com.br> *
* Copyright (C) 2001 Daniel Tauchke *
* All Rights Reserved *
* *
* This library and virtual machine is free software; you can redistribute *
* it and/or modify it under the terms of the Amended GNU Lesser Genegral *
* Public License distributed with this software. *
* *
* 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. *
* *
* For the purposes of the SuperWaba software we request that software using *
* or linking to the SuperWaba virtual machine or its libraries display the *
* following notice: *
* *
* Created with SuperWaba *
* http://www.superwaba.org *
* *
* Please see the software license located at SuperWabaSDK/license.txt *
* for more details. *
* *
* You should have received a copy of the License along with this software; *
* if not, write to *
* *
* Guilherme Campos Hazan *
* Av. Nossa Senhora de Copacabana 728 apto 605 - Copacabana *
* Rio de Janeiro / RJ - Brazil *
* Cep: 22050-000 *
* E-mail: support@superwaba.com.br *
* *
*******************************************************************************/
// ListBox.java written 2001 by Daniel Tauschke and a little modified by guich
// http://www.tauschke.com
// E-mail: tauschke@ tauschke.com
import waba.ui.*;
import waba.fx.*;
import waba.util.*;
import waba.sys.*;
/*******************************************************************************
*
* File: Tree.java
* Date: August 26,2004.
* Last Modified: September 5, 2004.
* Author: Tri (Trev) Quang Nguyen.
* Version: 0.9
* Email: tnguyen@ceb.nlm.nih.gov
*
* Description: This class is a simple implementation of a tree widget.
* Since it's natural to render the tree in rows, this class
* borrows most of the code from Waba's ListBox.
*
* Features:
*
* - similiar to Microsoft Window Explorer tree
* - horizontal and vertical scrolling
* - allows setting of folder and leaf icons.
* - angled line
* - expands and collapse of folder
* - allowsChildren flag to determine if the node is a leaf or folder
* - delete, insert, and modify (user object) of a node
* - clicking on leaf node will swap leaf icon (like hyperlink)
* - allows creation of tree to show or hide root node.
*
* Note: You should use TreeModel class to mofidy the tree after
*
********************************************************************************/
public class Tree extends Container{
//private static final Color red = new Color(255,0,0);
//private static final Color green = new Color(0, 255, 0);
//private static final Color blue = new Color(0,0,255);
//private static final Color yellow = new Color(255,255,200);
//private static final Color aqua = new Color(225,255,255);
protected Image imgPlus; // the expand icon "-"
protected Image imgMinus; // the expand icon "+"
protected Image imgOpen; // the open folder icon
protected Image imgClose; // the close folder icon
protected Image imgVisit; // the visited file icon
protected Image imgFile; // the unvisited file icon
protected ScrollBar vbar; // vertical scrollbar
protected ScrollBar hbar; // horizontal scrollbar
protected TreeModel model = null; // holds the (original) node tree structure
protected Vector items = new Vector(); // hold the nodes to be drawn
protected IntVector levels = new IntVector(); // hold the level (for faster rendering)
protected IntVector expands = new IntVector();
protected boolean simpleBorder; // used by PopList
protected int offset; // the vertical offset
protected int hsOffset; // the horizontal offset
protected int selectedIndex = -1; // the selected index
protected int itemCount; // the vertical scrollbar maximum (size of items vector)
protected int hsCount; // the horizontal scrollbar maximum
protected int visibleItems; // the first visible item display
protected int btnX; // the vertical scrollbar's x position
private int maxLevel = 0; // the max level to expand when setting the root node.
private boolean showRoot = false; // flag to show the root node or hide it.
private boolean allowsChildren = true; // flag to use the node's allowsChildren to determine if the node is a leaf or not
private Color fColor;
private Color bgColor0;
private Color bgColor1;
private Color cursorColor;
private Color fourColors[] = new Color[4];
private Graphics myg;
// variable used for x position to draw the icons and line connectors.
private int w1 = 0; // width of collapse[imgPlus] icon
private int h1 = 0; // height of collapse[imgPlus] icon
private int w2 = 0; // width of folder [imgOpen] icon
private int h2 = 0; // height of folder [imgOpen] icon
private int hline = 3; // number of pixel used to draw horizontal line (from plus icon to gap betwwen plus icon and folder or leaf icon)
private int gap = 2; // the number of space(in pixel) between the plus icon and the folder or leaf icon.
// scrollbars policies (not implemented yet - but still some bugs)
// 0 = always
// 1 = as needed
// 2 = never
private int hbarPolicy = 0;
private int vbarPolicy = 0;
/*********************************************************************
*********************************************************************/
public Tree(){ this(new TreeModel());}
/*********************************************************************
*********************************************************************/
public Tree(TreeModel model){ this(model, true); }
/*********************************************************************
*********************************************************************/
public Tree(TreeModel model, boolean showRoot){
super.add(vbar = new ScrollBar(ScrollBar.VERTICAL));
super.add(hbar = new ScrollBar(ScrollBar.HORIZONTAL));
vbar.setLiveScrolling(true);
hbar.setLiveScrolling(true);
this.showRoot = showRoot;
this.allowsChildren = (model == null)? true: model.getAllowsChildren();
setModel(model);
if (Settings.isColor)
setCursorColor(new Color(225,255,255)); // aqua (cursor color) highlight
}
/*********************************************************************
* Method to set the tree model.
* @param model the tree model.
*********************************************************************/
public void setModel(TreeModel model){
clear();
this.model = (model != null)? model: new TreeModel();
model.setTree(this);
initTree(model.getRoot());
}
/*********************************************************************
* Method to set the tree properties.
* Currently, only support setting the horizontal scrollbar policy
* @param policyType = "Tree.Horizontal_ScrollBar"
* @param policyValue = "ScrollBar_ALWAYS", "ScrollBar_AS_NEEDED", or "ScrollBar_NEVER"
*********************************************************************/
public void setPolicy(String policyType, String policyValue){
if (policyType.equals("Tree.Horizontal_ScrollBar")){
if (policyValue.equals("ScrollBar_ALWAYS")){
hbarPolicy = 0;
if (!hbar.isVisible())
resetScrollBars();
}
else if (policyValue.equals("ScrollBar_AS_NEEDED")){
hbarPolicy = 1;
resetScrollBars();
}
else if (policyValue.equals("ScrollBar_NEVER")){
hbarPolicy = 2;
hbar.setVisible(false);
}
}
}
/*********************************************************************
* Method to set the tree root node with the new root node. If the
* new root node is null, the tree is unchanged.
* @param root the new tree root node.
*********************************************************************/
public void initTree(Node root){
if (root != null){
if (showRoot){
items.add(root);
levels.add(0);
expands.add(0);
expand(root);
}
else {
Node childs[] = root.childrenArray();
for (int i = 0; i < childs.length; i++){
items.add(childs[i]);
levels.add(1);
expands.add(0);
}
}
}
resetScrollBars();
initImage();
}
/*********************************************************************
* Method to initialize the vertical and horizontal scrollbars maximum.
*********************************************************************/
protected void initScrollBars(){
// initialize the vertical scrollbar
itemCount = items.size();
vbar.setEnabled(enabled && visibleItems < itemCount);
vbar.setMaximum(itemCount); // guich@210_12: forgot this line!
// initialize the horizontal scrollbar
int maxWidth = 0;
for (int i = 0; i < this.items.size(); i++)
maxWidth = Math.max(getItemWidth(i), maxWidth);
maxWidth = maxWidth - (width - vbar.getPreferredWidth());
hsCount = (maxWidth > 0)? maxWidth: 0;
hbar.setEnabled(enabled && hsCount > width - vbar.getPreferredWidth());
hbar.setMaximum(hsCount);
switch (hbarPolicy){
case 0: hbar.setVisible(true); break;
case 1:
if (hsCount > width - vbar.getPreferredWidth()) hbar.setVisible(true);
else hbar.setVisible(false);
case 2: hbar.setVisible(false); break;
}
}
/*********************************************************************
* Method to load icons use for the tree. You can change the icon by
* using the setIcon(int iconType, Filename imageFilename).
*********************************************************************/
protected void initImage(){
try {
if (Settings.screenWidth > 160){
setIcon(0,"cePlus.bmp");
setIcon(1,"ceMinus.bmp");
setIcon(2,"ceClose.bmp");
setIcon(3,"ceOpen.bmp");
setIcon(4,"ceFile.bmp");
setIcon(5,"ceFileOpen.bmp");
}
else {
setIcon(0,"plus.bmp");
setIcon(1,"minus.bmp");
setIcon(2,"close.bmp");
setIcon(3,"open.bmp");
setIcon(4,"file.bmp");
setIcon(5,"fileOpen.bmp");
}
}
catch (Exception e){ /* TODO */ }
}
/*********************************************************************
* Method to set the icon of the tree based on the icon type.
* Note: You should not change the plus and minus icons.
* @param iconType 0 - plus icon "+"
* 1 - minus icon "-"
* 2 - open folder icon
* 3 - close folder icon
* 4 - file icon
* 5 - file opened (visited) icon
* @param filename the filename of the image to load.
* @throws Exception
*********************************************************************/
public void setIcon(int iconType, String filename) throws Exception {
Image img = new Image(filename);
switch (iconType){
case 0:
imgPlus = img;
w1 = img.getWidth();
h1 = img.getHeight();
break;
case 1: imgMinus = img; break;
case 2: imgClose = img; break;
case 3:
imgOpen = img;
w2 = img.getWidth();
h2 = img.getHeight();
break;
case 4: imgFile = img; break;
case 5: imgVisit = img; break;
}
}
/*********************************************************************
* Method to return the width of the given item index with the current
* fontmetrics.
* Note: if you overide this class you must implement this method.
*********************************************************************/
protected int getItemWidth(int index){
return fm.getTextWidth(items.items[index].toString());
}
/*********************************************************************
* Method to empties this Tree, setting all elements of the array to
* null, so they can be garbage collected.
*********************************************************************/
public void removeAll(){ // guich@210_13
model = new TreeModel();
clear();
}
/*********************************************************************
* Same as removeAll() method. Just more clearer method name
*********************************************************************/
public void clear(){
items.clear();
levels.clear();
expands.clear();
vbar.setMaximum(0);
hbar.setMaximum(0);
itemCount = 0;
hsCount = 0;
offset = 0; // wolfgang@330_23
hsOffset = 0;
selectedIndex = -1; // seanwalton@401_26
repaint();
}
/*********************************************************************
* Method to insert the items to the tree (For internal uses)
* Note: this method does not reset the scroll bar..you need to call this
* resetScrollBars() after you have performed an insert.
*********************************************************************/
private void insert(int index, Node node, int level, int expandValue){
items.insert(index, node);
levels.insert(index, level);
expands.insert(index, expandValue);
}
/*********************************************************************
* Method to remove the given index from the Tree items vector.
* This method will not remove the node from the original node.
* @param index the item index in the items vector.
*********************************************************************/
public void remove(int index){ // guich@200final_12: new method
if (index < 0 || index > itemCount-1) return;
int level = levels.items[index];
do {
items.del(index);
levels.del(index);
expands.del(index);
itemCount--;
} while (level < levels.items[index] && index < itemCount);
resetScrollBars();
repaint();
}
/*********************************************************************
* Method to remove an Object from the Tree's items vector.
* @paran the Node to delete from the tree's item vector.
*********************************************************************/
public void remove(Object item){
int index = items.find(item);
if (itemCount > 0 && index != -1)
remove(index);
}
/*********************************************************************
* Method to reset the horizontal scroll bar properties.
*********************************************************************/
private void resetHBar(){
if (hbarPolicy == 2) return;
// calculate the horizontalscrollbar maximum
int max = 0;
int indent = 3 + (w1+hline+w2/2-w1/2);
for (int i = 0; i < items.size(); i++){
max = Math.max(max, fm.getTextWidth( ((Node) items.items[i]).getNodeName()) + indent*levels.items[i]); // bug: calculation is off
}
max += vbar.getPreferredWidth(); // remember to take into account of the pixels used to draw the icons and scrollbar
hbar.setMaximum(max);
if (hbarPolicy == 0 || (width-vbar.getPreferredWidth()) < max) {
hbar.setEnabled(enabled && (width-vbar.getPreferredWidth()) < max);
hbar.setVisible(true);
}
else hbar.setVisible(false);
}
/*********************************************************************
* Method to reset the horizontal scroll bar properties.
*********************************************************************/
private void resetVBar(){
itemCount = items.size();
vbar.setMaximum(itemCount);
vbar.setEnabled(enabled && visibleItems < itemCount);
if (selectedIndex == itemCount) //last item was removed?
select(selectedIndex-1);
if ( itemCount == 0 ) // olie@200b4_196: if after removing the list has 0 items, select( -1 ) is called, which does nothing (see there), then selectedIndex keeps being 0 which is wrong, it has to be -1
selectedIndex = -1;
if (itemCount <= visibleItems && offset != 0) // guich@200final_13
offset = 0;
}
/*********************************************************************
* Method to rest the vertical and horizontal scrollbars properties.
* Note: there's still a bug in resetting the horizontal scroll bar.
*********************************************************************/
private void resetScrollBars(){
resetVBar();
resetHBar();
}
/*********************************************************************
* Method to expand a collapsed node.
* @param node the collapse node to expand.
*********************************************************************/
public void expand(Node node){
int index = indexOf(node);
if (index != -1 && expands.items[index] == 0 && !node.isLeaf(allowsChildren)){
expands.items[index] = 1;
Node childs[] = node.childrenArray();
for (int i = 0; i < childs.length; i++){
index++;
insert(index, childs[i], childs[i].getLevel(), 0);
}
resetScrollBars();
repaint();
}
}
/*********************************************************************
* Method to collapse an expanded node.
* @param node the expanded node to collapse.
*********************************************************************/
public void collapse(Node node){
int index = indexOf(node);
if (index != -1 && expands.items[index] == 1 && !node.isLeaf(allowsChildren)){
int level = levels.items[index];
index++;
while (index < itemCount && level < levels.items[index]){
levels.del(index);
items.del(index);
expands.del(index);
itemCount++;
}
expands.items[index-1] = 0;
resetScrollBars();
repaint();
}
}
/*********************************************************************
* Method to set the Object at the given Index, starting from 0.
* @param i the index
* @param s the object to set.
*********************************************************************/
public void setItemAt(int i, Object s){
if (0 <= i && i < itemCount){
items.items[i] = s;
repaint();
}
}
/*********************************************************************
* Method to get the Object at the given Index. Returns an empty
* string in case of error.
* @param i the index.
*********************************************************************/
public Object getItemAt(int i){
if (0 <= i && i < itemCount)
return items.items[i];//get(i);
return "";
}
/*********************************************************************
* Method to return the selected node from the tree. Return null if
* no selection has been made.
* @return the selected Node, or null is no selection has been made.
*********************************************************************/
public Node getSelectedNode(){
return selectedIndex >= 0 ? (Node)items.items[selectedIndex] : null; // guich@200b4: handle no selected index yet.
}
/*********************************************************************
* Method to return the selected item of the Tree or an empty String
* if no selection has been made.
* @return the selected object, or null is no selection has been made.
*********************************************************************/
public Object getSelectedItem(){
return selectedIndex >= 0 ? items.items[selectedIndex] : ""; // guich@200b4: handle no selected index yet.
}
/*********************************************************************
* Method to return the position of the selected item of the Tree or
* -1 if the Tree has no selected index yet.
* @return the selected index or -1 if no selection has been made.
*********************************************************************/
public int getSelectedIndex(){ return selectedIndex; }
/*********************************************************************
* Method to return all items in the items vector as an array of object.
* The objects are of the class Node.
* @return all items in items vector as an array of Objects.
*********************************************************************/
public Object []getItems(){ return items.toObjectArray(); }
/*********************************************************************
* Method to return the index of the item specified by the name, or -1
* if not found.
* @param name the object to find.
* @return the index of the item specified by the name, or -1 if not found.
*********************************************************************/
public int indexOf(Object name){ return items.find(name); }
/*********************************************************************
* Method to select the given name. If the name is not found, the current
* selected item is not changed.
* @since SuperWaba 4.01
* @param name the object to select.
*********************************************************************/
public void select(Object name){ // guich@401_25
int pos = indexOf(name);
if (pos != -1)
select(pos);
}
/*********************************************************************
* Method to select the given index and scroll to it if necessary.
* Note: select must be called only after the control has been added
* to the container and its rect has been set.
* @param i the index of the item.
*********************************************************************/
public void select(int i) {
if (0 <= i && i < itemCount && i != selectedIndex && height != 0){
offset=i;
int vi = vbar.getVisibleItems();
int ma = vbar.getMaximum();
if (offset+vi > ma) // astein@200b4_195: fix list items from being lost when the comboBox.select() method is used
offset=Math.max(ma-vi,0); // guich@220_4: fixed bug when the listbox is greater than the current item count
selectedIndex = i;
vbar.setValue(offset); // guich@210_9: fixed scrollbar update when selecting items
repaint();
}
else if (i == -1){ // guich@200b4_191: unselect all items
offset = 0;
vbar.setValue(0);
selectedIndex = -1;
repaint();
}
}
/*********************************************************************
* Returns the number of items (Nodes)
*********************************************************************/
public int size(){ return itemCount; }
/*********************************************************************
* Do nothing.
*********************************************************************/
public void add(Control control){ }
/*********************************************************************
* Do nothing.
*********************************************************************/
public void remove(Control control){ }
/*********************************************************************
* Method to return the preferred width, ie, size of the largest item plus 20.
* @return the preferred width of this control.
*********************************************************************/
public int getPreferredWidth(){
int maxWidth = 0;
int n = itemCount;
for (int i = 0; i < n; i++)
maxWidth = Math.max(getItemWidth(i), maxWidth);
return maxWidth + (simpleBorder?4:6) + vbar.getPreferredWidth();
}
/*********************************************************************
* Method to return the number of items multiplied by the font metrics
* height
* @return the preferred height of this control.
*********************************************************************/
public int getPreferredHeight() {
int n = itemCount;
int h = Math.max(fmH*n,vbar.getPreferredHeight())+(simpleBorder?4:6);
return n==1 ? h-1 : h;
}
/*********************************************************************
* Method to search this Tree for an item with the first letter matching
* the given char. The search is made case insensitive. Note: if you
* override this class you must implement this method.
*********************************************************************/
protected void find(char c) {
for (int i =0; i < itemCount; i++) {
String s = items.items[i].toString(); // guich@220_37
// first letter matches and not the already selected index?
if (s.length() > 0 && Convert.toUpperCase(s.charAt(0)) == c && selectedIndex != i){
select(i);
repaint();
break; // end the for loop
}
}
}
/*********************************************************************
* Method to enable this control if the specified enabled flag is true.
*********************************************************************/
public void setEnabled(boolean enabled){
if (enabled != this.enabled){
this.enabled = enabled;
onColorsChanged(false);
vbar.setEnabled(enabled && visibleItems < itemCount);
repaint(); // now the controls have different l&f for disabled states
}
}
/*********************************************************************
*********************************************************************/
protected void onColorsChanged(boolean colorsChanged){
if (colorsChanged)
vbar.setBackForeColors(backColor,foreColor);
fColor = getForeColor();
bgColor0 = getBackColor().brighter();
bgColor1 = cursorColor!=null?cursorColor:(bgColor0.equ != Color.WHITE.equ)?backColor:bgColor0.getCursorColor();//guich@300_20: use backColor instead of: bgColor0.getCursorColor(); // guich@210_19
if (fColor.equ == bgColor1.equ) // guich@200b4_206: ops! same color?
fColor = foreColor;
Graphics.compute3dColors(enabled,backColor,foreColor,fourColors);
}
/*********************************************************************
* Method to recalculate the box size for the selected item if the
* control is resized by the main application .
*********************************************************************/
protected void onBoundsChanged(){
int btnW = vbar.getPreferredWidth();
//int m = simpleBorder?1:2;
visibleItems = ((height-2 - hbar.getPreferredHeight()) / fmH);
vbar.setMaximum(itemCount);
vbar.setVisibleItems(visibleItems);
vbar.setEnabled(visibleItems < itemCount);
btnX = width - btnW;
//if (Settings.uiStyle == Settings.PalmOS) {btnX--; m++;}
vbar.setRect(btnX,0,btnW,height);
int hsH = hbar.getPreferredHeight();
hbar.setRect(0, height - hsH, width - btnW, hsH);
myg = createGraphics(); // guich@350_25: create a new myg
resetScrollBars();
repaint();
}
/*********************************************************************
* Method to notify the tree that a node has been removed from the tree
* model and to repaint the tree to reflect the changes..if necessary
* @param node the node that has been removed from the tree model
*********************************************************************/
public void nodeRemoved(Node node){
if (indexOf(node) != -1)
remove(node);
}
/*********************************************************************
* NOT IMPLEMENTED !!!!!!!!!!!!!!!!!!!!!!!!!!!
* Method to notify the tree that a node has been added to the tree model
* and to repaint the tree to reflect the changes..if necessary
* @param parent the parent node of the new added node
* @param child the new ly added node
* @param index the index of the new node
*********************************************************************/
public boolean nodeInserted(Node parent, Node child, int index){
int pos = indexOf(parent);
if (pos < 0) return false; // didn't find parent node
if (expands.items[pos] == 0) return false; // node is not expanded..so we don't have to paint the enode
int lvl = parent.getLevel() + 1;
int count = 0;
for (int i = pos+1; i < items.size(); i++){
if (lvl == levels.items[i]){
if (count == index){
insert(i, child, lvl, 0);
break;
}
count++;
}
else if (lvl > levels.items[i] || i == items.size()-1){
insert(i+1, child, lvl, 0);
break;
}
}
resetScrollBars();
repaint();
return true;
}
/*********************************************************************
* Method to notify the tree that a node in the tree model has been
* modified (currently - only changing the user object)
* @param node the node that has been modified
*********************************************************************/
public void nodeModified(Node node){
if (indexOf(node) != -1){
resetScrollBars();
repaint();
}
}
/*********************************************************************
*********************************************************************/
public void event_PEN_UP(PenEvent pe){
// Post the event
int sel = ((pe.y-(simpleBorder?3:4))/fmH) + offset; // guich@200b4_2: corrected line selection
if (contains(x+pe.x,y+pe.y) && pe.x < btnX && sel < itemCount){
postEvent(new ControlEvent(ControlEvent.PRESSED, this));
Node node = (Node) items.items[sel];
int level = levels.items[sel];
int xstart = 3 - hsOffset + (w1 + hline + gap + w2/2 - w1/2 ) * (level-1);
int xend = xstart + w1+hline+gap+w2;
if (node.isLeaf(allowsChildren)){
xstart += w1+hline+gap+1;
if (pe.x >= xstart && pe.x <= xend){
node.setVisited(true);
repaint();
}
}
else if (pe.x >= xstart && pe.x <= xend){
// call expand and collapse or change the leaf icon on when clicked on the icon or plus sign
if (expands.items[sel] == 0) expand(node);
else collapse(node);
}
else if (pe.x >= (xend+5) && pe.x < ((xend+5) + fm.getTextWidth(node.toString()))){
if (expands.items[sel] == 0) expand(node);
else collapse(node);
}
}
}
/*********************************************************************
*********************************************************************/
public void event_PEN_DRAG(PenEvent pe){ event_PEN_DOWN(pe); }
/*********************************************************************
*********************************************************************/
public void event_PEN_DOWN(PenEvent pe){
if (pe.x < btnX && contains(this.x+pe.x,this.y+pe.y)){ // && ((pe.y < fmH*drawItems) && (pe.y<fmH*itemCount-1)))
int sel = ((pe.y- (simpleBorder?3:4)) / fmH) + offset; // guich@200b4: corrected line selection
if (sel != selectedIndex && sel < itemCount){
if (selectedIndex >= 0)
drawCursor(myg,selectedIndex,false);
selectedIndex = sel;
drawCursor(myg,selectedIndex,true);
}
}
}
/*********************************************************************
*********************************************************************/
public void event_PRESSED(Event event){
if (event.target == vbar){
int newOffset = vbar.getValue();
if (newOffset != offset){ // guich@200final_3: avoid unneeded repaints
offset = newOffset;
repaint();
}
}
else if (event.target == hbar){
int hsValue = hbar.getValue();
if (hsValue != hsOffset && hsValue > -1){
hsOffset = hsValue;
repaint();
}
}
}
/*********************************************************************
*********************************************************************/
public void event_KEP_PRESS(Event event){
int key = ((KeyEvent)event).key;
if (key == IKeys.PAGE_UP ||
key == IKeys.PAGE_DOWN ||
key == IKeys.UP ||
key == IKeys.DOWN ||
key == IKeys.JOG_UP ||
key == IKeys.JOG_DOWN){ // guich@220_19 - guich@330_45
vbar.onEvent(event);
}
else if (key == IKeys.LEFT || key == IKeys.RIGHT)
hbar.onEvent(event);
else
find(Convert.toUpperCase((char)key));
}
/*********************************************************************
*********************************************************************/
public void onEvent(Event event){
PenEvent pe;
switch (event.type){
case ControlEvent.WINDOW_MOVED:
if (myg != null)
myg.free();
myg = createGraphics();
break;
case ControlEvent.PRESSED:
event_PRESSED(event);
break;
case PenEvent.PEN_DRAG:
case PenEvent.PEN_DOWN:
if (event.target != this) break;
event_PEN_DOWN((PenEvent)event);
break;
case KeyEvent.KEY_PRESS:
event_KEP_PRESS(event);
break;
case PenEvent.PEN_UP:
if (event.target != this) break;
event_PEN_UP((PenEvent)event);
break;
} // end switch
}
/*********************************************************************
*********************************************************************/
public void onPaint(Graphics g){
if (myg == null) myg = createGraphics();
// Draw background and borders
g.setBackColor(bgColor0);
g.fillRect(0,0,btnX,height);
g.setForeColor(foreColor);
if (simpleBorder && Settings.uiStyle == Settings.WinCE)
g.drawRect(0,0,width,height);
else
g.draw3dRect(0,0,width,height,(Settings.uiStyle == Settings.PalmOS)?Graphics.R3D_SHADED:Graphics.R3D_CHECK,false,false,fourColors);
// draw scrollbar border (why is it disappear in the first place? or is there a border for the scrollbar class??)
g.drawRect(btnX-1, 0, vbar.getPreferredWidth()+1, height);
if (hbar.isVisible())
g.drawRect(0, height-hbar.getPreferredHeight()-1, width-vbar.getPreferredWidth()+1, hbar.getPreferredHeight());
int dx = 3;
int dy = 3;
if (Settings.uiStyle == Settings.PalmOS) dy--;
if (simpleBorder) {dx--; dy--;}
g.setForeColor(fColor);
g.setClip(dx-1,dy-1,btnX-dx,fmH * visibleItems+1);
int greatestVisibleItemIndex = Math.min(itemCount, visibleItems+offset); // code corrected by Bjoem Knafla
for (int i = offset; i < greatestVisibleItemIndex; ++i, dy += fmH){
drawNode(g,i,dx-hsOffset,dy); // guich@200b4: let the user extend ListBox and draw itself the items
}
if (selectedIndex >= 0) drawCursor(g,selectedIndex,true);
}
/*********************************************************************
* Method to draw the icons and node text
*********************************************************************/
protected void drawNode(Graphics g, int index, int dx, int dy){
Node node = (Node) items.items[index];
int level = levels.items[index] - 1;
dy += 2;
drawConnector(g,index, dx,dy, node); // draw the line that connect the nodes
boolean expand = (expands.items[index] == 1);
int x = dx + (w1 + hline + gap + w2/2 - w1/2 ) * level;
int y = dy;
// draw plus minus icon
y = dy + fmH/2;
if (node.isLeaf(allowsChildren)) g.drawLine(x+w1/2, y, x + w1, y);
else if (node.getChildCount() == 0) g.drawLine(x+w1/2, y, x + w1, y);
else if (expand) g.drawImage(imgMinus, x, y - h1/2);
else g.drawImage(imgPlus, x, y - h1/2);
// draw horizontal line
x += w1;
g.drawLine(x, y, x+hline, y);
// draw folder icon (remember the gap needed)
x += hline + gap;
y = dy + fmH/2 - h2/2;
if (node.isLeaf(allowsChildren) && node.isVisited()) g.drawImage(imgVisit, x, y);
else if (node.isLeaf(allowsChildren)) g.drawImage(imgFile, x, y);
else if (expand) g.drawImage(imgOpen, x, y);
else g.drawImage(imgClose, x, y);
dy--;
x += w2 + gap;
y = dy;
/* Handle a potential list of user icons. */
int iconw;
if (Settings.screenWidth <= 160) {
iconw=0;
} else {
iconw=gap;
}
/* Figure out how many user icons there are. */
int n=0;
if (node.userIcons!=null) {
n=node.userIcons.size();
}
/* Loop through and display any icons found. */
for (int i = 0; i <n; i++) {
g.drawImage(((NodeIcon)(node.userIcons.items[i])).getImage(), x+iconw, y);
iconw+=((NodeIcon)(node.userIcons.items[i])).getWidth();
}
if (Settings.screenWidth>160)
iconw++;
node.wicons=iconw;
g.drawText(node.toString(), x+iconw,y); // guich@402_31: don't test for index out of bounds. this will be catched in the caller
}
/*********************************************************************
* Method to draw the (line connector) angled line.
*********************************************************************/
protected void drawConnector(Graphics g, int index, int dx, int dy, Node node){
if (node == null ) return;
int level = node.getLevel() - 1; //levels.items[index] - 1;
Node prev = node.getPreviousSibling();
Node next = node.getNextSibling();
// calculate the x-start position
int x = dx;
if (level == 0) x += w1/2;
else if (level == 1) x += w1 + hline + gap + w2/2;
else x += w1 + hline + gap + w2/2 + (w1/2 + hline + gap + w2/2 + 1) * (level-1);
// calculate the y-start and y-end position
int ystart;
int yend;
// handles the last level 1 node
if (level == 0 && next == null){
if (prev != null && items.items[index] == node){
ystart = dy - (fmH-h1)/2;
yend = dy + (fmH-h1)/2;
g.drawLine(x, ystart, x, yend);
}
}
// draw vertical connector lines for leaf node
if (node.isLeaf(allowsChildren) || node.getChildCount() == 0){
ystart = dy - (fmH-h2)/2;
yend = dy + (fmH/2);
g.drawLine(x, ystart, x, yend);
if (next != null){
ystart = yend;
yend += (fmH/2);
g.drawLine(x, ystart, x, yend);
}
}
// draw vertical connector lines for folder node
else {
if (next == null && node == items.items[index]){
ystart = dy - (fmH-h1)/2;
yend = dy + (fmH-h1)/2;
g.drawLine(x, ystart, x, yend); // draw from "+" to end of line
}
if (next != null){
ystart = dy - (fmH-h1)/2;
yend = dy + fmH;
g.drawLine(x, ystart , x, yend);
}
}
drawConnector(g,index,dx,dy, node.getParent());
}
/*********************************************************************
* Method to draw the highlight box when user select a listbox's item.
*********************************************************************/
protected void drawCursor(Graphics g, int sel, boolean on){
if (offset <= sel && sel < visibleItems+offset && sel < itemCount){
int level = levels.items[sel];
int pw = imgPlus.getWidth();
int dx = 3 - hsOffset;
if (level == 1) dx += (w1+hline+gap+w2) * level;
else dx += w1+hline+gap+w2 + (w1+hline+gap+w2/2-w1/2) * (level-1);
if (Settings.screenWidth > 160) dx += 3;
int dy = 4;
if (Settings.uiStyle == Settings.PalmOS) dy--;
if (simpleBorder) {dx--; dy--;}
dy += (sel-offset) * fmH;
g.setClip(dx-1,dy-1,btnX-dx,Math.min(fmH * visibleItems, this.height-dy)); // guich@200b4_83: fixed selection overflowing paint area
g.setForeColor(on?bgColor0:bgColor1);
g.setBackColor(on?bgColor1:bgColor0);
g.eraseRect(dx+1,dy,fm.getTextWidth(((Node) items.items[sel]).toString()) +
+((Node) items.items[sel]).wicons +2,fmH+fm.descent-1); // only select the Object - guich@200b4_130
}
}
/*********************************************************************
* Method to set the cursor color for this Tree. The default is
* equal to the background slightly darker. Make sure you tested it
* in 2,4 and 8bpp devices.
*********************************************************************/
public void setCursorColor(Color color){
this.cursorColor = color;
onColorsChanged(true);
}
/*********************************************************************
* Method to set the listbox's border to be not 3d if flag is true.
*********************************************************************/
public void setSimpleBorder(boolean simpleBorder){ // guich@200b4_93
this.simpleBorder = simpleBorder;
}
/*********************************************************************
* Method to reload the tree.
* Use this method when the tree model has made a drastic change.
*********************************************************************/
public void reload(){
clear();
initTree(model.getRoot());
repaint();
}
/*********************************************************************
* Method to clear the tree and release the tree model references.
*********************************************************************/
public void unload(){
clear();
model = null;
}
/*********************************************************************
* Method to display a message to the system console.
*********************************************************************/
public void echo(Object msg){ Vm.debug(msg.toString()); }
/*********************************************************************
* Debug: convient method to diplay the contents of the items vector.
*********************************************************************/
public void display(){
for (int i = 0; i < items.size(); i++){
Node node = (Node) items.items[i];
//Vm.debug(node.toString() + " [" + levels.items[i] + "," + expands.items[i] + "]");
}
}
}