Tree.java
上传用户:haobig99
上传日期:2022-06-15
资源大小:369k
文件大小:9k
源码类别:

J2ME

开发平台:

Java

  1. /*
  2.  * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
  3.  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4.  *
  5.  * This code is free software; you can redistribute it and/or modify it
  6.  * under the terms of the GNU General Public License version 2 only, as
  7.  * published by the Free Software Foundation.  Sun designates this
  8.  * particular file as subject to the "Classpath" exception as provided
  9.  * by Sun in the LICENSE file that accompanied this code.
  10.  *
  11.  * This code is distributed in the hope that it will be useful, but WITHOUT
  12.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14.  * version 2 for more details (a copy is included in the LICENSE file that
  15.  * accompanied this code).
  16.  *
  17.  * You should have received a copy of the GNU General Public License version
  18.  * 2 along with this work; if not, write to the Free Software Foundation,
  19.  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20.  *
  21.  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22.  * CA 95054 USA or visit www.sun.com if you need additional information or
  23.  * have any questions.
  24.  */
  25. package com.sun.lwuit.tree;
  26. import com.sun.lwuit.Button;
  27. import com.sun.lwuit.Component;
  28. import com.sun.lwuit.Container;
  29. import com.sun.lwuit.Display;
  30. import com.sun.lwuit.Image;
  31. import com.sun.lwuit.Label;
  32. import com.sun.lwuit.animations.CommonTransitions;
  33. import com.sun.lwuit.events.ActionEvent;
  34. import com.sun.lwuit.events.ActionListener;
  35. import com.sun.lwuit.geom.Dimension;
  36. import com.sun.lwuit.layouts.BorderLayout;
  37. import com.sun.lwuit.layouts.BoxLayout;
  38. import com.sun.lwuit.plaf.Style;
  39. import com.sun.lwuit.util.EventDispatcher;
  40. import java.util.Vector;
  41. /**
  42.  * The tree component allows constructing simple tree component hierechies that can be expaneded seamingly
  43.  * with no limit. The tree is bound to a model that can provide data with free form depth such as file system
  44.  * or similarly structured data.
  45.  * To customize the look of the tree the component can be derived and component creation can be replaced.
  46.  *
  47.  * @author Shai Almog
  48.  */
  49. public class Tree extends Container {
  50.     private static final String KEY_OBJECT = "TREE_OBJECT";
  51.     private static final String KEY_PARENT = "TREE_PARENT";
  52.     private static final String KEY_EXPANDED = "TREE_NODE_EXPANDED";
  53.     private static final String KEY_DEPTH = "TREE_DEPTH";
  54.     private EventDispatcher leafListener = new EventDispatcher();
  55.     private ActionListener expansionListener = new Handler();
  56.     private TreeModel model;
  57.     private static Image folder;
  58.     private static Image openFolder;
  59.     private static Image nodeImage;
  60.     private int depthIndent = 15;
  61.     /**
  62.      * Construct a tree with the given tree model
  63.      *
  64.      * @param model represents the contents of the tree
  65.      */
  66.     public Tree(TreeModel model) {
  67.         this.model = model;
  68.         setLayout(new BoxLayout(BoxLayout.Y_AXIS));
  69.         buildBranch(null, 0, this);
  70.         setScrollableY(true);
  71.         setUIID("Tree");
  72.     }
  73.     /**
  74.      * Sets the icon for a tree folder 
  75.      * 
  76.      * @param folderIcon the icon for a folder within the tree
  77.      */
  78.     public static void setFolderIcon(Image folderIcon) {
  79.         folder = folderIcon;
  80.     }
  81.     /**
  82.      * Sets the icon for a tree folder in its expanded state
  83.      *
  84.      * @param folderIcon the icon for a folder within the tree
  85.      */
  86.     public static void setFolderOpenIcon(Image folderIcon) {
  87.         openFolder = folderIcon;
  88.     }
  89.     /**
  90.      * Sets the icon for a tree node
  91.      *
  92.      * @param nodeIcon the icon for a node within the tree
  93.      */
  94.     public static void setNodeIcon(Image nodeIcon) {
  95.         nodeImage = nodeIcon;
  96.     }
  97.     private void expandNode(Component c) {
  98.         c.putClientProperty(KEY_EXPANDED, "true");
  99.         ((Button)c).setIcon(openFolder);
  100.         int depth = ((Integer)c.getClientProperty(KEY_DEPTH)).intValue();
  101.         Container parent = c.getParent();
  102.         Object o = c.getClientProperty(KEY_OBJECT);
  103.         Container dest = new Container(new BoxLayout(BoxLayout.Y_AXIS));
  104.         Label dummy = new Label();
  105.         parent.addComponent(BorderLayout.CENTER, dummy);
  106.         buildBranch(o, depth, dest);
  107.         parent.replace(dummy, dest, CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, true, 300));
  108.     }
  109.     private void collapseNode(Component c) {
  110.         c.putClientProperty(KEY_EXPANDED, null);
  111.         ((Button)c).setIcon(folder);
  112.         Container p = c.getParent();
  113.         for(int iter = 0 ; iter < p.getComponentCount() ; iter++) {
  114.             if(p.getComponentAt(iter) != c) {
  115.                 Label dummy = new Label();
  116.                 p.replaceAndWait(p.getComponentAt(iter), dummy, CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, false, 300));
  117.                 p.removeComponent(dummy);
  118.             }
  119.         }
  120.     }
  121.     /**
  122.      * Returns the currently selected item in the tree
  123.      *
  124.      * @return the object selected within the tree
  125.      */
  126.     public Object getSelectedItem() {
  127.         Component c = getComponentForm().getFocused();
  128.         if(c != null) {
  129.             return c.getClientProperty(KEY_OBJECT);
  130.         }
  131.         return null;
  132.     }
  133.     /**
  134.      * Adds the child components of a tree branch to the given container.
  135.      */
  136.     private void buildBranch(Object parent, int depth, Container destination) {
  137.         Vector children = model.getChildren(parent);
  138.         int size = children.size();
  139.         Integer depthVal = new Integer(depth + 1);
  140.         for(int iter = 0 ; iter < size ; iter++) {
  141.             final Object current = children.elementAt(iter);
  142.             Button nodeComponent = createNodeComponent(current, depth);
  143.             if(model.isLeaf(current)) {
  144.                 destination.addComponent(nodeComponent);
  145.                 nodeComponent.addActionListener(new Handler(current));
  146.             } else {
  147.                 Container componentArea = new Container(new BorderLayout());
  148.                 componentArea.addComponent(BorderLayout.NORTH, nodeComponent);
  149.                 destination.addComponent(componentArea);
  150.                 nodeComponent.addActionListener(expansionListener);
  151.             }
  152.             nodeComponent.putClientProperty(KEY_OBJECT, current);
  153.             nodeComponent.putClientProperty(KEY_PARENT, parent);
  154.             nodeComponent.putClientProperty(KEY_DEPTH, depthVal);
  155.         }
  156.     }
  157.     /**
  158.      * Creates a node within the tree, this method is protected allowing tree to be
  159.      * subclassed to replace the rendering logic of individual tree buttons.
  160.      *
  161.      * @param node the node object from the model to display on the button
  162.      * @param depth the depth within the tree (normally represented by indenting the entry)
  163.      * @return a button representing the node within the tree
  164.      */
  165.     protected Button createNodeComponent(Object node, int depth) {
  166.         Button cmp = new Button(childToDisplayLabel(node));
  167.         cmp.setUIID("TreeNode");
  168.         if(model.isLeaf(node)) {
  169.             cmp.setIcon(nodeImage);
  170.         } else {
  171.             cmp.setIcon(folder);
  172.         }
  173.         updateNodeComponentStyle(cmp.getSelectedStyle(), depth);
  174.         updateNodeComponentStyle(cmp.getUnselectedStyle(), depth);
  175.         updateNodeComponentStyle(cmp.getPressedStyle(), depth);
  176.         return cmp;
  177.     }
  178.     private void updateNodeComponentStyle(Style s, int depth) {
  179.         s.setMargin(LEFT, depth * depthIndent);
  180.     }
  181.     /**
  182.      * Converts a tree child to a label, this method can be overriden for
  183.      * simple rendering effects
  184.      *
  185.      * @return a string representing the given tree node
  186.      */
  187.     protected String childToDisplayLabel(Object child) {
  188.         return child.toString();
  189.     }
  190.     /**
  191.      * A listener that fires when a leaf is clicked
  192.      *
  193.      * @param l listener to fire when the leaf is clicked
  194.      */
  195.     public void addLeafListener(ActionListener l) {
  196.         leafListener.addListener(l);
  197.     }
  198.     /**
  199.      * Removes the listener that fires when a leaf is clicked
  200.      *
  201.      * @param l listener to remove
  202.      */
  203.     public void removeLeafListener(ActionListener l) {
  204.         leafListener.removeListener(l);
  205.     }
  206.     /**
  207.      * @inheritDoc
  208.      */
  209.     protected Dimension calcPreferredSize() {
  210.         Dimension d = super.calcPreferredSize();
  211.         // if the tree is entirely collapsed try to reserve at least 6 rows for the content
  212.         int count = getComponentCount();
  213.         for(int iter = 0 ; iter < count ; iter++) {
  214.             if(getComponentAt(iter) instanceof Container) {
  215.                 return d;
  216.             }
  217.         }
  218.         int size = model.getChildren(null).size();
  219.         if(size < 6) {
  220.             return new Dimension(Math.max(d.getWidth(), Display.getInstance().getDisplayWidth() / 4 * 3),
  221.                     d.getHeight() / size * 6);
  222.         }
  223.         return d;
  224.     }
  225.     /**
  226.      * This class unifies two action listeners into a single class to reduce the size overhead
  227.      */
  228.     private class Handler implements ActionListener {
  229.         private Object current;
  230.         public Handler() {
  231.         }
  232.         public Handler(Object current) {
  233.             this.current = current;
  234.         }
  235.         public void actionPerformed(ActionEvent evt) {
  236.             if(current != null) {
  237.                 leafListener.fireActionEvent(new ActionEvent(current));
  238.                 return;
  239.             }
  240.             Component c = (Component)evt.getSource();
  241.             Object e = c.getClientProperty(KEY_EXPANDED);
  242.             if(e != null && e.equals("true")) {
  243.                 collapseNode(c);
  244.             } else {
  245.                 expandNode(c);
  246.             }
  247.         }
  248.     }
  249. }