Handle hostnames with upper-case letters
[webmin.git] / file / Hierarchy.java.bak
1 // Hierarchy
2 // An AWT component for displaying a tree-like heirachy, with each node
3 // having an icon and a name. This heirachy can be expanded or contracted
4 // by the user.
5 import java.awt.*;
6 import java.util.Vector;
7
8 public class Hierarchy extends BorderPanel implements CbScrollbarCallback
9 {
10         HierarchyNode root;             // the root of the tree
11         CbScrollbar sb;                 // scrollbar at right
12         int width, height;              // usable drawing area
13         int sbwidth;                    // size of scrollbar
14         HierarchyCallback callback;     // who to call on open / close
15         Image bim;                      // double-buffer image
16         Font font = new Font("courier", Font.PLAIN, 12);
17         FontMetrics fnm;                // size of font used
18         Graphics bg;                    // back-images graphics
19         int top = 0;                    // top-most row displayed
20         int count = 0;                  // total rows in the tree
21         Insets in;                      // insets from border
22         HierarchyNode sel;              // selected node
23         long last;                      // time of last mouse click
24         static boolean broken_awt = System.getProperty("os.name").
25                                     startsWith("Windows");
26
27         // Create a new Hierarchy object with the given root
28         Hierarchy(HierarchyNode r)
29         {
30         this();
31         root = r;
32         }
33
34         // Create a new Hierarchy object that calls back to the given object
35         // when nodes are clicked on.
36         Hierarchy(HierarchyNode r, HierarchyCallback c)
37         {
38         this(r);
39         callback = c;
40         }
41
42         // Create an empty hierarchy object, with no callback
43         Hierarchy()
44         {
45         super(3, new Color(50,50,50), new Color(220,220,220));
46
47         // Create UI
48         setLayout(null);
49         sb = new CbScrollbar(CbScrollbar.VERTICAL, this);
50         add(sb);
51         }
52
53         // Create an empty hierarchy object, set to report user actions to
54         // the given object.
55         Hierarchy(HierarchyCallback c)
56         {
57         this();
58         callback = c;
59         }
60
61         // redraw
62         // Called by the using class when the tree passed to this object
63         // changes, to force a redraw and resizing of the scrollbar
64         void redraw()
65         {
66         if (fnm != null) {
67                 render();
68                 paint(getGraphics());
69                 compscroll();
70                 }
71         }
72
73         // setRoot
74         // Set the root node for this hierarchy
75         void setRoot(HierarchyNode r)
76         {
77         root = r;
78         redraw();
79         }
80
81         // selected
82         // Return the currently selected node, or null
83         HierarchyNode selected()
84         {
85         return sel;
86         }
87
88         // select
89         // Selected the given node
90         void select(HierarchyNode s)
91         {
92         sel = s;
93         }
94
95         // force the use of some font
96         void setFont(Font f)
97         {
98         font = f;
99         bim = null;
100         repaint();
101         }
102
103         // reshape
104         // Called when this component gets resized
105         public void reshape(int nx, int ny, int nw, int nh)
106         {
107         in = insets();
108         sbwidth = sb.minimumSize().width;
109         width = nw-sbwidth - (in.left + in.right);
110         height = nh - (in.top + in.bottom);
111         sb.reshape(width+in.left, in.top, sbwidth, height);
112
113         // force creation of a new backing images
114         bim = null;
115         repaint();
116         compscroll();
117
118         super.reshape(nx, ny, nw, nh);
119         }
120
121         // update
122         // Called sometime after repaint()
123         public void update(Graphics g)
124         {
125         render();
126         paint(g);
127         }
128
129         // paint
130         // Blit the backing image to the front
131         public void paint(Graphics g)
132         {
133         super.paint(g);
134         if (bim == null) {
135                 // This is the first rendering
136                 bim = createImage(width, height);
137                 bg = bim.getGraphics();
138                 bg.setFont(font);
139                 fnm = bg.getFontMetrics();
140                 render();
141                 compscroll();
142                 }
143         g.drawImage(bim, in.left, in.top, this);
144         }
145
146         // mouseDown
147         // Called upon a mouseclick
148         public boolean mouseDown(Event evt, int x, int y)
149         {
150         if (root == null)
151                 return false;           // nothing to do
152         HierarchyNode s = nodeat(root, x/16, (y/16)+top);
153         if (s == null) {
154                 // Just deselect
155                 sel = null;
156                 repaint();
157                 return true;
158                 }
159
160         // Check for double-click
161         boolean dc = false;
162         if (evt.when-last < 500 && sel == s)
163                 dc = true;
164         else
165                 last = evt.when;
166         sel = s;
167
168         if (dc && sel.ch != null) {
169                 // Open or close this node
170                 sel.open = !sel.open;
171                 if (callback != null) {
172                         // Notify callback, which MAY do something to change
173                         // the structure of the tree
174                         if (sel.open) callback.openNode(this, sel);
175                         else          callback.closeNode(this, sel);
176                         }
177                 }
178         else if (callback != null) {
179                 // Single click on a node or double-click on leaf node
180                 if (dc) callback.doubleNode(this, sel);
181                 else    callback.clickNode(this, sel);
182                 }
183         compscroll();
184         repaint();
185         return true;
186         }
187
188         public void moved(CbScrollbar s, int v)
189         {
190         moving(s, v);
191         }
192
193         public void moving(CbScrollbar s, int v)
194         {
195         top = sb.getValue();
196         compscroll();
197         repaint();
198         }
199
200         // render
201         // Draw the current tree view into the backing image
202         private void render()
203         {
204         if (fnm != null) {
205                 int fh = fnm.getHeight(),       // useful font metrics
206                     fa = fnm.getAscent();
207                 bg.setColor(Color.white);
208                 bg.fillRect(0, 0, width, height);
209                 if (root == null)
210                         return;         // nothing to do
211                 bg.setColor(Color.black);
212                 recurse(root, 0, 0, fh, fa);
213                 }
214         }
215
216         // recurse
217         // Render a node in the tree at the given location, maybe followed
218         // by all it's children. Return the number of rows this node took
219         // to display.
220         private int recurse(HierarchyNode n, int x, int y, int fh, int fa)
221         {
222         int xx = x*16, yy = (y-top)*16;
223         int len = 1;
224
225         n.x = x;
226         n.y = y;
227         int tw = fnm.stringWidth(n.text);
228         if (yy >= 0 && yy <= height) {
229                 // Draw this node
230                 if (n.im != null)
231                         bg.drawImage(n.im, xx, yy, this);
232                 if (sel == n) {
233                         // Select this node
234                         bg.setColor(Color.lightGray);
235                         bg.fillRect(xx+17, yy+2, tw+2, 13);
236                         bg.setColor(Color.black);
237                         }
238                 bg.drawString(n.text, xx+18, yy+12);
239                 }
240         if (n.ch != null && n.open && yy <= height) {
241                 // Mark this node
242                 bg.drawLine(xx+18, yy+14, xx+17+tw, yy+14);
243
244                 // Draw subnodes
245                 yy += 16;
246                 for(int i=0; i<n.ch.size() && yy<=height; i++) {
247                         int l=recurse((HierarchyNode)n.ch.elementAt(i),
248                                       x+1, y+len, fh, fa);
249                         bg.drawLine(xx+7, yy+7, xx+15, yy+7);
250                         if (i == n.ch.size()-1)
251                                 bg.drawLine(xx+7, yy, xx+7, yy+7);
252                         else
253                                 bg.drawLine(xx+7, yy, xx+7,yy+(l*16)-1);
254                         len += l;
255                         yy += l*16;
256                         }
257                 }
258         return len;
259         }
260
261         // compscroll
262         // Re-compute scrollbar size
263         private void compscroll()
264         {
265         if (fnm == null)
266                 return;
267         int ct = root!=null ? count(root) : 1;
268         int r = Math.min(ct, height/16 - 1);
269         int c = ct - r;
270         //sb.setValues(top, r==0?1:r, c<0?0:c);
271         sb.setValues(top, r==0?1:r, ct);
272         }
273
274         // count
275         // Returns the number of visible rows from a node
276         private int count(HierarchyNode n)
277         {
278         int l = 1;
279         if (n.open && n.ch != null)
280                 for(int i=0; i<n.ch.size(); i++)
281                         l += count((HierarchyNode)n.ch.elementAt(i));
282         return l;
283         }
284
285         // nodeat
286         // Is the given node at the given position? If not, check its
287         // children too.
288         private HierarchyNode nodeat(HierarchyNode n, int x, int y)
289         {
290         if (y == n.y && x >= n.x)
291                 return n;
292         if (n.ch == null || !n.open)
293                 return null;
294         for(int i=0; i<n.ch.size(); i++) {
295                 HierarchyNode c = nodeat((HierarchyNode)n.ch.elementAt(i),x,y);
296                 if (c != null) return c;
297                 }
298         return null;
299         }
300 }
301
302 // HierarchyNode
303 // One node in the tree displayed by the Hierarchy object.
304 class HierarchyNode
305 {
306         boolean open;           // is this node open?
307         Image im;               // icon for this node (assumed to be 16x16!)
308         Vector ch;              // sub-nodes of this one, or null
309         String text;            // name of this node
310         int x, y;               // row/column in list
311
312         HierarchyNode() { }
313
314         HierarchyNode(boolean o, Image i, Vector c, String t)
315         {
316         open = o;
317         im = i;
318         ch = c;
319         text = t;
320         }
321 }
322
323 // HierarchyCallback
324 // Programmers using the Hierarchy class pass an object that implements the
325 // HierarchyCallback interface to its constructor, to receive information
326 // about user actions.
327 interface HierarchyCallback
328 {
329         // openNode
330         // Called when a node with children is opened
331         void openNode(Hierarchy h, HierarchyNode n);
332
333         // closeNode
334         // Called when a node is closed
335         void closeNode(Hierarchy h, HierarchyNode n);
336
337         // clickNode
338         // Called when the user clicks on a node
339         void clickNode(Hierarchy h, HierarchyNode n);
340
341         // doubleNode
342         // Called when a user double-clicks on a node
343         void doubleNode(Hierarchy h, HierarchyNode n);
344 }
345