Java tutorial
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); } } } } }