TreeLinkTest.java Source code

Java tutorial

Introduction

Here is the source code for TreeLinkTest.java

Source

import java.applet.Applet;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Label;
import java.awt.LayoutManager;
import java.awt.List;
import java.awt.Rectangle;
import java.awt.TextField;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 * Applet GUI demo of Gamelan TreeLayout layout manager. Constructs a tree and,
 * for each entry, makes a button that jumps to it.
 * 
 * The input language is a file like this: R Java Resources # root L - Resources
 * at Sun # label B http://www.sun.com/foo/bar Interesting Stuff # URLbutton B
 * http://javasoft.com/b/c More Stuff # URLbutton
 * 
 * The result is (supposedly) a beautiful(?) tree. Each L is a top-level label,
 * and each B is in the tree below it.
 * 
 * Could be made much fancier with getParameter("FontName"), "FontSize",
 * adjusting width with fontMetrics, etc. Works adequately for now.
 */
public class TreeLinkTest extends Applet {
    TreeLayout tl;

    public void init() {
        tl = new TreeLayout();
        setLayout(tl);
        Button root = new Button("This is the root");
        add("Root", root);
        tl.setRoot(root);
        Component x = new Label("A random label");
        add("label", x);
        tl.setParent(x, root);
        Component y;
        y = new TextField("Add any component");
        add("comp", y);
        tl.setParent(y, root);
        x = new List();
        ((List) x).add("List entry");
        ((List) x).add("Similarly useless list entry");
        add("list", x);
        tl.setParent(x, root);
        x = new Button("Extremely long and unnecessary button title");
        add("button", x);
        tl.setParent(x, y);
        x = new MyCanvas(getImage(getDocumentBase(), "icons/tools.gif"));
        add("image", x);
        tl.setParent(x, y);
    }

    public void paint(Graphics g) {
        super.paint(g);
        tl.paintLines(g, getBackground());
    }

    class MyCanvas extends Canvas {
        Image img;

        MyCanvas(Image img) {
            this.img = img;
        }

        public Dimension getPreferredSize() {
            return new Dimension(64, 64);
        }

        public void update(Graphics g) {
            paint(g);
        }

        public void paint(Graphics g) {
            g.drawImage(img, 0, getSize().height / 2 - 16, 32, 32, this);
        }
    }
}

/**
 * Simple layout manager that arranges its children in a tree. The tree always
 * expands to fill the available area, and the internal components are resized
 * to fit the area proportional to their preferred size and the actual available
 * size.
 * 
 * This layout manager requires several method calls beyond the normal layout
 * manager. Please notice the following: * Components must be added using the
 * add(String, Component) method. The strings don't have to be unique, but must
 * be present. * Each instance must have exactly one root object, which must be
 * add()ed, <I>then </I> setRoot()ed. * Each component after the root must first
 * be added and then must be connected into the tree using setParent(child,
 * parent). * If you want lines between parents and children, you <EM>must
 * </EM> call paintLines() from your applet's paint() method.
 * 
 * @author name unknown, xxx@blackdown.org
 */

class TreeLayout implements LayoutManager {
    TreeNode root;

    Hashtable nodes;

    public TreeLayout() {
        nodes = new Hashtable();
    }

    public void addLayoutComponent(String name, Component comp) {
        TreeNode tn = new TreeNode(comp);
        nodes.put(comp, tn);
    }

    public void removeLayoutComponent(Component comp) {
        nodes.remove(comp);
    }

    /**
     * You <em>must</em> make this call, otherwise none of the components will
     * be layed out.
     */
    public void setRoot(Component c) {
        root = (TreeNode) nodes.get(c);
    }

    /**
     * Sets the tree parent of a child. The components <em>must</em> have been
     * previously added. If either component has not previously been added, this
     * injunction is silently ignored. Cycles are <em>not</em> checked.
     */
    public void setParent(Component child, Component parent) {
        TreeNode p = (TreeNode) nodes.get(parent);
        TreeNode c = (TreeNode) nodes.get(child);
        if ((p != null) && (c != null))
            p.addChild(c);
    }

    public Dimension minimumLayoutSize(Container target) {
        Dimension d = root.getMinimumSize();
        Insets insets = target.getInsets();
        d.width += insets.left + insets.right;
        d.height += insets.top + insets.bottom;
        return d;
    }

    public Dimension preferredLayoutSize(Container target) {
        Dimension d = root.getPreferredSize();
        Insets insets = target.getInsets();
        d.width += insets.left + insets.right;
        d.height += insets.top + insets.bottom;
        return d;
    }

    public void layoutContainer(Container target) {
        Insets insets = target.getInsets();
        Dimension d = target.getSize();
        Dimension root_pref = root.getPreferredSize();
        double xscale = ((double) (d.width - insets.left - insets.right) / (double) (root_pref.width));
        double yscale = ((double) (d.height - insets.top - insets.bottom) / (double) (root_pref.height));
        root.doLayout(xscale, yscale, insets.left, insets.top);
    }

    /**
     * This piece of hackery is needed since one cant really draw things from a
     * layout manager. Call this if you want to draw lines between components.
     */
    public void paintLines(Graphics g, Color bg) {
        Color dk = bg.darker();
        Color br = bg.brighter();
        root.paintLines(g, dk, br);
    }
}

class TreeNode {
    Component comp;

    Vector children;

    Dimension prefSz, minSz;

    TreeNode(Component comp) {
        super();
        this.comp = comp;
        children = new Vector();
    }

    Dimension getMinimumSize() {
        if (!comp.isVisible())
            return new Dimension(0, 0);
        if (minSz == null) {
            Dimension d = comp.getMinimumSize();
            minSz = new Dimension(d.width, d.height);

            if (children.size() > 0) {
                for (Enumeration e = children.elements(); e.hasMoreElements();) {
                    TreeNode t = (TreeNode) (e.nextElement());
                    if (t.comp.isVisible()) {
                        d = t.getMinimumSize();
                        minSz.height += d.height;
                        minSz.width = Math.max(d.width, minSz.width);
                    }
                }
            }
        }
        return minSz;
    }

    Dimension getPreferredSize() {
        if (!comp.isVisible())
            return new Dimension(0, 0);
        if (prefSz == null) {
            Dimension d = comp.getPreferredSize();
            prefSz = new Dimension(d.width, d.height);

            if (children.size() > 0) {
                int wmax = 0;
                for (Enumeration e = children.elements(); e.hasMoreElements();) {
                    TreeNode t = (TreeNode) (e.nextElement());
                    if (t.comp.isVisible()) {
                        d = t.getPreferredSize();
                        prefSz.height += d.height;
                        if (wmax < d.width) {
                            wmax = d.width;
                        }
                    }
                }
                prefSz.width += wmax;
            }
        }
        return prefSz;
    }

    void addChild(TreeNode t) {
        children.addElement(t);
        prefSz = null;
        minSz = null;
    }

    void removeChild(TreeNode t) {
        children.removeElement(t);
        prefSz = null;
        minSz = null;
    }

    void paintLines(Graphics g, Color dk, Color br) {
        if (comp.isVisible()) {
            Rectangle b = comp.getBounds();
            int x1off = b.x + b.width / 2;
            int x2off = b.x + b.width;
            int y1off = b.y;
            for (Enumeration e = children.elements(); e.hasMoreElements();) {
                TreeNode tn = (TreeNode) (e.nextElement());
                if (tn.comp.isVisible()) {
                    Rectangle bn = tn.comp.getBounds();
                    int y2off = bn.y + bn.height / 2;
                    g.setColor(dk);
                    g.drawLine(x1off, y1off, x1off, y2off);
                    g.drawLine(x1off, y2off - 1, x2off, y2off - 1);
                    g.setColor(br);
                    g.drawLine(x1off + 1, y1off, x1off + 1, y2off);
                    g.drawLine(x1off, y2off, x2off, y2off);
                    tn.paintLines(g, dk, br);
                }
            }
        }
    }

    void doLayout(double xscale, double yscale, int x, int y) {
        // x and y are the offsets into the
        // Container where we start doing the
        // goodies for this Node
        if (comp.isVisible()) {
            Dimension pref = comp.getPreferredSize();
            int ht = (int) Math.round(yscale * pref.height);
            int wd = (int) Math.round(xscale * pref.width);

            ht = (pref.height < ht) ? pref.height : ht;
            wd = (pref.width < wd) ? pref.width : wd;

            comp.setBounds(x, y, wd, ht);
            y += ht;
            x += wd;

            for (Enumeration e = children.elements(); e.hasMoreElements();) {
                TreeNode tn = (TreeNode) (e.nextElement());
                if (tn.comp.isVisible()) {
                    pref = tn.getPreferredSize();
                    tn.doLayout(xscale, yscale, x, y);
                    y += (int) Math.round(yscale * pref.height);

                }
            }
        }
    }
}