source: src/main/java/weka/gui/treevisualizer/TreeVisualizer.java @ 5

Last change on this file since 5 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 66.8 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 *    TreeVisualizer.java
19 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.treevisualizer;
24
25import weka.core.Instances;
26import weka.core.Utils;
27import weka.gui.visualize.PrintablePanel;
28import weka.gui.visualize.VisualizePanel;
29import weka.gui.visualize.VisualizeUtils;
30
31import java.awt.Color;
32import java.awt.Container;
33import java.awt.Dimension;
34import java.awt.Font;
35import java.awt.FontMetrics;
36import java.awt.Graphics;
37import java.awt.Graphics2D;
38import java.awt.event.ActionEvent;
39import java.awt.event.ActionListener;
40import java.awt.event.ItemEvent;
41import java.awt.event.ItemListener;
42import java.awt.event.MouseEvent;
43import java.awt.event.MouseListener;
44import java.awt.event.MouseMotionListener;
45import java.io.FileReader;
46import java.io.IOException;
47import java.io.StringReader;
48import java.util.Properties;
49
50import javax.swing.BorderFactory;
51import javax.swing.ButtonGroup;
52import javax.swing.JDialog;
53import javax.swing.JFrame;
54import javax.swing.JMenu;
55import javax.swing.JMenuItem;
56import javax.swing.JOptionPane;
57import javax.swing.JPopupMenu;
58import javax.swing.JRadioButton;
59import javax.swing.JRadioButtonMenuItem;
60import javax.swing.JTextField;
61import javax.swing.Timer;
62
63/**
64 * Class for displaying a Node structure in Swing. <p>
65 *
66 * To work this class simply create an instance of it.<p>
67 *
68 * Assign it to a window or other such object.<p>
69 *
70 * Resize it to the desired size.<p>
71 *
72 *
73 * When using the Displayer hold the left mouse button to drag the
74 * tree around. <p>
75 *
76 * Click the left mouse button with ctrl to shrink the size of the tree
77 * by half. <p>
78 *
79 * Click and drag with the left mouse button and shift to draw a box,
80 * when the left mouse button is released the contents of the box
81 * will be magnified
82 * to fill the screen. <p> <p>
83 *
84 * Click the right mouse button to bring up a menu. <p>
85 * Most options are self explanatory.<p>
86 *
87 * Select Auto Scale to set the tree to it's optimal display size.
88 *
89 * @author Malcolm Ware (mfw4@cs.waikato.ac.nz)
90 * @version $Revision: 4960 $
91 */
92public class TreeVisualizer
93  extends PrintablePanel
94  implements MouseMotionListener, MouseListener, ActionListener, ItemListener {
95
96  /** for serialization */
97  private static final long serialVersionUID = -8668637962504080749L;
98
99  /** the props file. */
100  public final static String PROPERTIES_FILE = "weka/gui/treevisualizer/TreeVisualizer.props";
101 
102  /** The placement algorithm for the Node structure. */
103  private NodePlace m_placer; 
104
105  /** The top Node. */
106  private Node m_topNode;
107 
108  /** The postion of the view relative to the tree. */
109  private Dimension m_viewPos;       
110
111  /** The size of the tree in pixels. */
112  private Dimension m_viewSize;     
113 
114  /** The font used to display the tree. */
115  private Font m_currentFont;       
116
117  /** The size information for the current font. */
118  private FontMetrics m_fontSize;   
119
120  /** The number of Nodes in the tree. */
121  private int m_numNodes;
122
123  /** The number of levels in the tree. */
124  private int m_numLevels;     
125
126  /** An array with the Nodes sorted into it and display information
127   * about the Nodes. */
128  private NodeInfo[] m_nodes;
129
130  /** An array with the Edges sorted into it and display information
131   * about the Edges. */
132  private EdgeInfo[] m_edges;
133                     
134  /** A timer to keep the frame rate constant. */
135  private Timer m_frameLimiter;     
136                         
137  /** Describes the action the user is performing. */
138  private int m_mouseState;           
139
140  /** A variable used to tag the start pos of a user action. */
141  private Dimension m_oldMousePos;
142
143  /** A variable used to tag the most current point of a user action. */
144  private Dimension m_newMousePos;
145
146  /** A variable used to determine for the clicked method if any other
147   * mouse state has already taken place. */
148  private boolean m_clickAvailable;
149
150  /** A variable used to remember the desired view pos. */
151  private Dimension m_nViewPos;     
152
153  /** A variable used to remember the desired tree size. */
154  private Dimension m_nViewSize;       
155
156  /** The number of frames left to calculate. */
157  private int m_scaling;         
158
159  /** A right (or middle) click popup menu. */
160  private JPopupMenu m_winMenu;
161
162  /** An option on the win_menu */
163  private JMenuItem m_topN;
164
165  /** An option on the win_menu*/
166  private JMenuItem m_fitToScreen;
167
168  /** An option on the win_menu */
169  private JMenuItem m_autoScale;
170
171  /** A sub group on the win_menu */
172  private JMenu m_selectFont;
173
174  /** A grouping for the font choices */
175  private ButtonGroup m_selectFontGroup;
176
177  /** A font choice. */
178  private JRadioButtonMenuItem m_size24;
179
180  /** A font choice. */
181  private JRadioButtonMenuItem m_size22;
182
183  /** A font choice. */
184  private JRadioButtonMenuItem m_size20;
185
186  /** A font choice. */
187  private JRadioButtonMenuItem m_size18;
188
189  /** A font choice. */
190  private JRadioButtonMenuItem m_size16;
191
192  /** A font choice. */
193  private JRadioButtonMenuItem m_size14;
194
195  /** A font choice. */
196  private JRadioButtonMenuItem m_size12;
197
198  /** A font choice. */
199  private JRadioButtonMenuItem m_size10;
200
201  /** A font choice. */
202  private JRadioButtonMenuItem m_size8;
203
204  /** A font choice. */
205  private JRadioButtonMenuItem m_size6;
206
207  /** A font choice. */
208  private JRadioButtonMenuItem m_size4;
209
210  /** A font choice. */
211  private JRadioButtonMenuItem m_size2;
212
213  /** A font choice. */
214  private JRadioButtonMenuItem m_size1;
215
216  /** An option on the win menu. */
217  private JMenuItem m_accept;
218
219  /** A right or middle click popup menu for nodes. */
220  private JPopupMenu m_nodeMenu;
221
222  /** A visualize choice for the node, may not be available. */
223  private JMenuItem m_visualise;
224
225  /**
226   * An add children to Node choice, This is only available if the tree
227   * display has a treedisplay listerner added to it.
228   */
229  private JMenuItem m_addChildren;
230
231  /** Similar to add children but now it removes children. */
232  private JMenuItem m_remChildren;
233
234  /** Use this to have J48 classify this node. */
235  private JMenuItem m_classifyChild;
236 
237  /** Use this to dump the instances from this node to the vis panel. */
238  private JMenuItem m_sendInstances;
239
240  /** The subscript for the currently selected node (this is an internal
241   * thing, so the user is unaware of this). */
242  private int m_focusNode;
243
244  /**
245   * The Node the user is currently focused on , this is similar to
246   * focus node except that it is used by other
247   * classes rather than this one.
248   */
249  private int m_highlightNode;
250 
251  /* A pointer to this tree's classifier if a classifier is using it. */
252  //private UserClassifier classer;
253  private TreeDisplayListener m_listener;
254
255  private JTextField m_searchString;
256  private JDialog m_searchWin;
257  private JRadioButton m_caseSen;
258
259  /** the font color. */
260  protected Color m_FontColor = null;
261
262  /** the background color. */
263  protected Color m_BackgroundColor = null;
264
265  /** the node color. */
266  protected Color m_NodeColor = null;
267
268  /** the line color. */
269  protected Color m_LineColor = null;
270
271  /** the color of the zoombox. */
272  protected Color m_ZoomBoxColor = null;
273
274  /** the XOR color of the zoombox. */
275  protected Color m_ZoomBoxXORColor = null;
276 
277  /** whether to show the border or not. */
278  protected boolean m_ShowBorder = true;
279 
280  ///////////////////
281
282  //this is the event fireing stuff
283
284
285  /**
286   * Constructs Displayer to display a tree provided in a dot format.
287   * Uses the NodePlacer to place the Nodes.
288   * @param tdl listener
289   * @param dot string containing the dot representation of the tree to
290   * display
291   * @param p the algorithm to be used to position the nodes.
292   */
293  public TreeVisualizer(TreeDisplayListener tdl, String dot, NodePlace p) {
294    super();
295
296    initialize();
297   
298    //generate the node structure in here
299    if (m_ShowBorder)
300      setBorder(BorderFactory.createTitledBorder("Tree View")); 
301    m_listener = tdl;
302
303    TreeBuild builder = new TreeBuild();
304   
305    Node n = null;
306    NodePlace arrange = new PlaceNode2();
307    n = builder.create(new StringReader(dot));
308    //    System.out.println(n.getCount(n, 0));
309    //if the size needs to be automatically alocated I will do it here
310    m_highlightNode = 5;
311    m_topNode = n;
312    m_placer = p;
313    m_placer.place(m_topNode);
314    m_viewPos = new Dimension(0, 0);    //will be adjusted
315    m_viewSize = new Dimension(800, 600);   //I allocate this now so that
316    //the tree will be visible
317    //when the panel is enlarged
318
319    m_nViewPos = new Dimension(0, 0);           
320    m_nViewSize = new Dimension(800, 600);         
321                                     
322    m_scaling = 0;
323   
324    m_numNodes = m_topNode.getCount(m_topNode,0);   //note the second
325    //argument must be a zero, this is a
326    //recursive function
327
328    m_numLevels = m_topNode.getHeight(m_topNode,0);
329 
330    m_nodes = new NodeInfo[m_numNodes];
331    m_edges = new EdgeInfo[m_numNodes-1];
332   
333
334    arrayFill(m_topNode, m_nodes, m_edges);
335   
336    changeFontSize(12);
337
338    m_mouseState = 0;
339    m_oldMousePos = new Dimension(0, 0);
340    m_newMousePos = new Dimension(0, 0);
341    m_frameLimiter = new Timer(120, this);
342
343
344
345    m_winMenu = new JPopupMenu();
346    m_topN = new JMenuItem("Center on Top Node");           //note to change
347    //language change this line
348    m_topN.setActionCommand("Center on Top Node");          //but not this one,
349    //same for all menu items
350    m_fitToScreen = new JMenuItem("Fit to Screen");
351    m_fitToScreen.setActionCommand("Fit to Screen");
352    //unhide = new JMenuItem("Unhide all Nodes");
353    m_selectFont = new JMenu("Select Font");
354    m_selectFont.setActionCommand("Select Font");
355    m_autoScale = new JMenuItem("Auto Scale");
356    m_autoScale.setActionCommand("Auto Scale");
357    m_selectFontGroup = new ButtonGroup();
358   
359    m_accept = new JMenuItem("Accept The Tree");
360    m_accept.setActionCommand("Accept The Tree");
361   
362    m_winMenu.add(m_topN);
363    m_winMenu.addSeparator();
364    m_winMenu.add(m_fitToScreen);
365    m_winMenu.add(m_autoScale);
366    //m_winMenu.addSeparator();
367    //m_winMenu.add(unhide);
368    m_winMenu.addSeparator();
369    m_winMenu.add(m_selectFont);
370
371    if (m_listener != null) {
372      m_winMenu.addSeparator();
373      m_winMenu.add(m_accept);
374    }
375   
376    m_topN.addActionListener(this);
377    m_fitToScreen.addActionListener(this);
378    //unhide.addActionListener(this);
379    m_autoScale.addActionListener(this);
380    m_accept.addActionListener(this);
381       
382    m_size24 = new JRadioButtonMenuItem("Size 24",false);//,select_font_group);
383    m_size22 = new JRadioButtonMenuItem("Size 22",false);//,select_font_group);
384    m_size20 = new JRadioButtonMenuItem("Size 20",false);//,select_font_group);
385    m_size18 = new JRadioButtonMenuItem("Size 18",false);//,select_font_group);
386    m_size16 = new JRadioButtonMenuItem("Size 16",false);//,select_font_group);
387    m_size14 = new JRadioButtonMenuItem("Size 14",false);//,select_font_group);
388    m_size12 = new JRadioButtonMenuItem("Size 12",true);//,select_font_group);
389    m_size10 = new JRadioButtonMenuItem("Size 10",false);//,select_font_group);
390    m_size8 = new JRadioButtonMenuItem("Size 8",false);//,select_font_group);
391    m_size6 = new JRadioButtonMenuItem("Size 6",false);//,select_font_group);
392    m_size4 = new JRadioButtonMenuItem("Size 4",false);//,select_font_group);
393    m_size2 = new JRadioButtonMenuItem("Size 2",false);//,select_font_group);
394    m_size1 = new JRadioButtonMenuItem("Size 1",false);//,select_font_group);
395
396    m_size24.setActionCommand("Size 24");//,select_font_group);
397    m_size22.setActionCommand("Size 22");//,select_font_group);
398    m_size20.setActionCommand("Size 20");//,select_font_group);
399    m_size18.setActionCommand("Size 18");//,select_font_group);
400    m_size16.setActionCommand("Size 16");//,select_font_group);
401    m_size14.setActionCommand("Size 14");//,select_font_group);
402    m_size12.setActionCommand("Size 12");//,select_font_group);
403    m_size10.setActionCommand("Size 10");//,select_font_group);
404    m_size8.setActionCommand("Size 8");//,select_font_group);
405    m_size6.setActionCommand("Size 6");//,select_font_group);
406    m_size4.setActionCommand("Size 4");//,select_font_group);
407    m_size2.setActionCommand("Size 2");//,select_font_group);
408    m_size1.setActionCommand("Size 1");//,select_font_group);
409   
410   
411    m_selectFontGroup.add(m_size24);
412    m_selectFontGroup.add(m_size22);
413    m_selectFontGroup.add(m_size20);
414    m_selectFontGroup.add(m_size18);
415    m_selectFontGroup.add(m_size16);
416    m_selectFontGroup.add(m_size14);
417    m_selectFontGroup.add(m_size12);
418    m_selectFontGroup.add(m_size10);
419    m_selectFontGroup.add(m_size8);
420    m_selectFontGroup.add(m_size6);
421    m_selectFontGroup.add(m_size4);
422    m_selectFontGroup.add(m_size2);
423    m_selectFontGroup.add(m_size1);
424
425   
426    m_selectFont.add(m_size24);
427    m_selectFont.add(m_size22);
428    m_selectFont.add(m_size20);
429    m_selectFont.add(m_size18);
430    m_selectFont.add(m_size16);
431    m_selectFont.add(m_size14);
432    m_selectFont.add(m_size12);
433    m_selectFont.add(m_size10);
434    m_selectFont.add(m_size8);
435    m_selectFont.add(m_size6);
436    m_selectFont.add(m_size4);
437    m_selectFont.add(m_size2);
438    m_selectFont.add(m_size1);
439
440
441    m_size24.addItemListener(this);
442    m_size22.addItemListener(this);
443    m_size20.addItemListener(this);
444    m_size18.addItemListener(this);
445    m_size16.addItemListener(this);
446    m_size14.addItemListener(this);
447    m_size12.addItemListener(this);
448    m_size10.addItemListener(this);
449    m_size8.addItemListener(this);
450    m_size6.addItemListener(this);
451    m_size4.addItemListener(this);
452    m_size2.addItemListener(this);
453    m_size1.addItemListener(this);
454
455    /*
456      search_string = new JTextField(22);
457      search_win = new JDialog();
458      case_sen = new JRadioButton("Case Sensitive");
459
460
461
462      search_win.getContentPane().setLayout(null);
463      search_win.setSize(300, 200);
464 
465      search_win.getContentPane().add(search_string);
466      search_win.getContentPane().add(case_sen);
467
468      search_string.setLocation(50, 70);
469      case_sen.setLocation(50, 120);
470      case_sen.setSize(100, 24);
471      search_string.setSize(100, 24);
472      //search_string.setVisible(true);
473      //case_sen.setVisible(true);
474
475      //search_win.setVisible(true);
476    */
477
478    m_nodeMenu = new JPopupMenu();
479    /* A visualize choice for the node, may not be available. */
480    m_visualise = new JMenuItem("Visualize The Node");
481    m_visualise.setActionCommand("Visualize The Node");
482    m_visualise.addActionListener(this);
483    m_nodeMenu.add(m_visualise);
484   
485    if (m_listener != null) {
486      m_remChildren = new JMenuItem("Remove Child Nodes");
487      m_remChildren.setActionCommand("Remove Child Nodes");
488      m_remChildren.addActionListener(this);
489      m_nodeMenu.add(m_remChildren);
490     
491     
492      m_classifyChild = new JMenuItem("Use Classifier...");
493      m_classifyChild.setActionCommand("classify_child");
494      m_classifyChild.addActionListener(this);
495      m_nodeMenu.add(m_classifyChild);
496     
497      /*m_sendInstances = new JMenuItem("Add Instances To Viewer");
498      m_sendInstances.setActionCommand("send_instances");
499      m_sendInstances.addActionListener(this);
500      m_nodeMenu.add(m_sendInstances); */
501     
502    }
503   
504    m_focusNode = -1;
505    m_highlightNode = -1;
506   
507    addMouseMotionListener(this);
508    addMouseListener(this);
509    //repaint();
510    //frame_limiter.setInitialDelay();
511    m_frameLimiter.setRepeats(false);
512    m_frameLimiter.start();
513  }
514 
515  /**
516   * Constructs Displayer with the specified Node as the top
517   * of the tree, and uses the NodePlacer to place the Nodes.
518   * @param tdl listener.
519   * @param n the top Node of the tree to be displayed.
520   * @param p the algorithm to be used to position the nodes.
521   */ 
522  public TreeVisualizer(TreeDisplayListener tdl, Node n, NodePlace p) {
523    super();
524
525    initialize();
526   
527    //if the size needs to be automatically alocated I will do it here
528    if (m_ShowBorder)
529      setBorder(BorderFactory.createTitledBorder("Tree View")); 
530    m_listener = tdl;
531    m_topNode = n;
532    m_placer = p;
533    m_placer.place(m_topNode);
534    m_viewPos = new Dimension(0, 0);    //will be adjusted
535    m_viewSize = new Dimension(800, 600);   //I allocate this now so that
536    //the tree will be visible
537    //when the panel is enlarged
538
539    m_nViewPos = new Dimension(0, 0);           
540    m_nViewSize = new Dimension(800, 600);         
541                                     
542    m_scaling = 0;
543   
544    m_numNodes = m_topNode.getCount(m_topNode,0);   //note the second argument
545    //must be a zero, this is a
546    //recursive function
547
548    m_numLevels = m_topNode.getHeight(m_topNode,0);
549 
550    m_nodes = new NodeInfo[m_numNodes];
551    m_edges = new EdgeInfo[m_numNodes-1];
552
553    arrayFill(m_topNode, m_nodes, m_edges);
554   
555    changeFontSize(12);
556
557    m_mouseState = 0;
558    m_oldMousePos = new Dimension(0, 0);
559    m_newMousePos = new Dimension(0, 0);
560    m_frameLimiter = new Timer(120, this);
561
562
563
564
565
566    m_winMenu = new JPopupMenu();
567    m_topN = new JMenuItem("Center on Top Node");           //note to change
568    //language change this line
569    m_topN.setActionCommand("Center on Top Node");          //but not this
570    //one, same for all menu items
571    m_fitToScreen = new JMenuItem("Fit to Screen");
572    m_fitToScreen.setActionCommand("Fit to Screen");
573    //unhide = new JMenuItem("Unhide all Nodes");
574    m_selectFont = new JMenu("Select Font");
575    m_selectFont.setActionCommand("Select Font");
576    m_autoScale = new JMenuItem("Auto Scale");
577    m_autoScale.setActionCommand("Auto Scale");
578    m_selectFontGroup = new ButtonGroup();
579   
580    m_accept = new JMenuItem("Accept The Tree");
581    m_accept.setActionCommand("Accept The Tree");
582   
583    m_winMenu.add(m_topN);
584    m_winMenu.addSeparator();
585    m_winMenu.add(m_fitToScreen);
586    m_winMenu.add(m_autoScale);
587    m_winMenu.addSeparator();
588    //m_winMenu.add(unhide);
589    m_winMenu.addSeparator();
590    m_winMenu.add(m_selectFont);
591    m_winMenu.addSeparator();
592
593    if (m_listener != null) {
594      m_winMenu.add(m_accept);
595    }
596   
597    m_topN.addActionListener(this);
598    m_fitToScreen.addActionListener(this);
599    //unhide.addActionListener(this);
600    m_autoScale.addActionListener(this);
601    m_accept.addActionListener(this);
602       
603    m_size24 = new JRadioButtonMenuItem("Size 24",false);//,select_font_group);
604    m_size22 = new JRadioButtonMenuItem("Size 22",false);//,select_font_group);
605    m_size20 = new JRadioButtonMenuItem("Size 20",false);//,select_font_group);
606    m_size18 = new JRadioButtonMenuItem("Size 18",false);//,select_font_group);
607    m_size16 = new JRadioButtonMenuItem("Size 16",false);//,select_font_group);
608    m_size14 = new JRadioButtonMenuItem("Size 14",false);//,select_font_group);
609    m_size12 = new JRadioButtonMenuItem("Size 12",true);//,select_font_group);
610    m_size10 = new JRadioButtonMenuItem("Size 10",false);//,select_font_group);
611    m_size8 = new JRadioButtonMenuItem("Size 8",false);//,select_font_group);
612    m_size6 = new JRadioButtonMenuItem("Size 6",false);//,select_font_group);
613    m_size4 = new JRadioButtonMenuItem("Size 4",false);//,select_font_group);
614    m_size2 = new JRadioButtonMenuItem("Size 2",false);//,select_font_group);
615    m_size1 = new JRadioButtonMenuItem("Size 1",false);//,select_font_group);
616
617    m_size24.setActionCommand("Size 24");//,select_font_group);
618    m_size22.setActionCommand("Size 22");//,select_font_group);
619    m_size20.setActionCommand("Size 20");//,select_font_group);
620    m_size18.setActionCommand("Size 18");//,select_font_group);
621    m_size16.setActionCommand("Size 16");//,select_font_group);
622    m_size14.setActionCommand("Size 14");//,select_font_group);
623    m_size12.setActionCommand("Size 12");//,select_font_group);
624    m_size10.setActionCommand("Size 10");//,select_font_group);
625    m_size8.setActionCommand("Size 8");//,select_font_group);
626    m_size6.setActionCommand("Size 6");//,select_font_group);
627    m_size4.setActionCommand("Size 4");//,select_font_group);
628    m_size2.setActionCommand("Size 2");//,select_font_group);
629    m_size1.setActionCommand("Size 1");//,select_font_group);
630
631
632
633   
634   
635    m_selectFontGroup.add(m_size24);
636    m_selectFontGroup.add(m_size22);
637    m_selectFontGroup.add(m_size20);
638    m_selectFontGroup.add(m_size18);
639    m_selectFontGroup.add(m_size16);
640    m_selectFontGroup.add(m_size14);
641    m_selectFontGroup.add(m_size12);
642    m_selectFontGroup.add(m_size10);
643    m_selectFontGroup.add(m_size8);
644    m_selectFontGroup.add(m_size6);
645    m_selectFontGroup.add(m_size4);
646    m_selectFontGroup.add(m_size2);
647    m_selectFontGroup.add(m_size1);
648
649
650
651   
652    m_selectFont.add(m_size24);
653    m_selectFont.add(m_size22);
654    m_selectFont.add(m_size20);
655    m_selectFont.add(m_size18);
656    m_selectFont.add(m_size16);
657    m_selectFont.add(m_size14);
658    m_selectFont.add(m_size12);
659    m_selectFont.add(m_size10);
660    m_selectFont.add(m_size8);
661    m_selectFont.add(m_size6);
662    m_selectFont.add(m_size4);
663    m_selectFont.add(m_size2);
664    m_selectFont.add(m_size1);
665
666
667    m_size24.addItemListener(this);
668    m_size22.addItemListener(this);
669    m_size20.addItemListener(this);
670    m_size18.addItemListener(this);
671    m_size16.addItemListener(this);
672    m_size14.addItemListener(this);
673    m_size12.addItemListener(this);
674    m_size10.addItemListener(this);
675    m_size8.addItemListener(this);
676    m_size6.addItemListener(this);
677    m_size4.addItemListener(this);
678    m_size2.addItemListener(this);
679    m_size1.addItemListener(this);
680
681
682
683
684    /*
685      search_string = new JTextField(22);
686      search_win = new JDialog();
687      case_sen = new JRadioButton("Case Sensitive");
688
689
690
691      search_win.getContentPane().setLayout(null);
692      search_win.setSize(300, 200);
693 
694      search_win.getContentPane().add(search_string);
695      search_win.getContentPane().add(case_sen);
696
697      search_string.setLocation(50, 70);
698      case_sen.setLocation(50, 120);
699      case_sen.setSize(100, 24);
700      search_string.setSize(100, 24);
701      //search_string.setVisible(true);
702      //case_sen.setVisible(true);
703
704      search_win.setVisible(true);
705    */
706
707
708    m_nodeMenu = new JPopupMenu();
709    /* A visualize choice for the node, may not be available. */
710    m_visualise = new JMenuItem("Visualize The Node");
711    m_visualise.setActionCommand("Visualize The Node");
712    m_visualise.addActionListener(this);
713    m_nodeMenu.add(m_visualise);
714
715    if (m_listener != null) {
716      m_remChildren = new JMenuItem("Remove Child Nodes");
717      m_remChildren.setActionCommand("Remove Child Nodes");
718      m_remChildren.addActionListener(this);
719      m_nodeMenu.add(m_remChildren);
720     
721      m_classifyChild = new JMenuItem("Use Classifier...");
722      m_classifyChild.setActionCommand("classify_child");
723      m_classifyChild.addActionListener(this);
724      m_nodeMenu.add(m_classifyChild);
725     
726      m_sendInstances = new JMenuItem("Add Instances To Viewer");
727      m_sendInstances.setActionCommand("send_instances");
728      m_sendInstances.addActionListener(this);
729      m_nodeMenu.add(m_sendInstances);
730     
731     
732    }
733 
734    m_focusNode = -1;
735    m_highlightNode = -1;
736   
737
738    addMouseMotionListener(this);
739    addMouseListener(this);
740
741 
742    //repaint();
743
744    //frame_limiter.setInitialDelay();
745    m_frameLimiter.setRepeats(false);
746    m_frameLimiter.start();
747  }
748
749  /**
750   * Processes the color string. Returns null if empty.
751   *
752   * @param colorStr    the string to process
753   * @return            the processed color or null
754   */
755  protected Color getColor(String colorStr) {
756    Color       result;
757   
758    result = null;
759   
760    if ((colorStr != null) && (colorStr.length() > 0))
761      result = VisualizeUtils.processColour(colorStr, result);
762   
763    return result;
764  }
765 
766  /**
767   * Performs some initialization.
768   */
769  protected void initialize() {
770    Properties  props;
771   
772    try {
773      props = Utils.readProperties(PROPERTIES_FILE);
774    }
775    catch (Exception e) {
776      e.printStackTrace();
777      props = new Properties();
778    }
779   
780    m_FontColor       = getColor(props.getProperty("FontColor", ""));
781    m_BackgroundColor = getColor(props.getProperty("BackgroundColor", ""));
782    m_NodeColor       = getColor(props.getProperty("NodeColor", ""));
783    m_LineColor       = getColor(props.getProperty("LineColor", ""));
784    m_ZoomBoxColor    = getColor(props.getProperty("ZoomBoxColor", ""));
785    m_ZoomBoxXORColor = getColor(props.getProperty("ZoomBoxXORColor", ""));
786    m_ShowBorder      = Boolean.parseBoolean(props.getProperty("ShowBorder", "true"));
787  }
788
789  /**
790   * Fits the tree to the current screen size. Call this after
791   * window has been created to get the entrire tree to be in view
792   * upon launch.
793   */
794  public void fitToScreen() {
795
796    getScreenFit(m_viewPos, m_viewSize);
797    repaint();
798  }
799
800  /**
801   * Calculates the dimensions needed to fit the entire tree into view.
802   */
803  private void getScreenFit(Dimension np, Dimension ns) {
804
805    int leftmost = 1000000, rightmost = -1000000;
806    int leftCenter = 1000000, rightCenter = -1000000, rightNode = 0;
807    int highest = -1000000, highTop = -1000000;
808    for (int noa = 0; noa < m_numNodes; noa++) {
809      calcScreenCoords(noa);
810      if (m_nodes[noa].m_center - m_nodes[noa].m_side < leftmost) {
811        leftmost = m_nodes[noa].m_center - m_nodes[noa].m_side;
812      }
813      if (m_nodes[noa].m_center < leftCenter) {
814        leftCenter = m_nodes[noa].m_center;
815      }
816     
817      if (m_nodes[noa].m_center + m_nodes[noa].m_side > rightmost) {
818        rightmost = m_nodes[noa].m_center + m_nodes[noa].m_side;         
819      }
820      if (m_nodes[noa].m_center > rightCenter) {
821        rightCenter = m_nodes[noa].m_center;
822        rightNode = noa;
823      }
824        if (m_nodes[noa].m_top + m_nodes[noa].m_height > highest) {
825          highest = m_nodes[noa].m_top + m_nodes[noa].m_height;
826        }
827        if (m_nodes[noa].m_top > highTop) {
828          highTop = m_nodes[noa].m_top;
829        }
830    }
831   
832    ns.width = getWidth();
833    ns.width -= leftCenter - leftmost + rightmost - rightCenter + 30;
834    ns.height = getHeight() - highest + highTop - 40;
835   
836    if (m_nodes[rightNode].m_node.getCenter() != 0 
837        && leftCenter != rightCenter) {
838        ns.width /= m_nodes[rightNode].m_node.getCenter();
839    }
840    if (ns.width < 10)
841      {
842        ns.width = 10;
843      }
844    if (ns.height < 10)
845      {
846        ns.height = 10;
847      }
848   
849    np.width = (leftCenter - leftmost + rightmost - rightCenter) / 2 + 15;
850    np.height = (highest - highTop) / 2 + 20;
851  }
852
853  /**
854   * Performs the action associated with the ActionEvent.
855   *
856   * @param e the action event.
857   */
858  public void actionPerformed(ActionEvent e) {
859   
860    //JMenuItem m = (JMenuItem)e.getSource();
861   
862    if (e.getActionCommand() == null) {
863      if (m_scaling == 0) {
864        repaint();
865      }
866      else {
867        animateScaling(m_nViewPos, m_nViewSize, m_scaling);
868      }
869    }
870    else if (e.getActionCommand().equals("Fit to Screen")) {
871     
872      Dimension np = new Dimension();
873      Dimension ns = new Dimension();
874
875      getScreenFit(np, ns);
876
877      animateScaling(np, ns, 10);
878     
879    }
880    else if (e.getActionCommand().equals("Center on Top Node")) {
881     
882      int tpx = (int)(m_topNode.getCenter() * m_viewSize.width);   //calculate
883      //the top nodes postion but don't adjust for where
884      int tpy = (int)(m_topNode.getTop() * m_viewSize.height);     //view is
885     
886     
887     
888      Dimension np = new Dimension(getSize().width / 2 - tpx, 
889                                   getSize().width / 6 - tpy);
890     
891      animateScaling(np, m_viewSize, 10);
892     
893    }
894    else if (e.getActionCommand().equals("Auto Scale")) {
895      autoScale();  //this will figure the best scale value
896      //keep the focus on the middle of the screen and call animate
897    }
898    else if (e.getActionCommand().equals("Visualize The Node")) {
899      //send the node data to the visualizer
900      if (m_focusNode >= 0) {
901        Instances inst;
902        if ((inst = m_nodes[m_focusNode].m_node.getInstances()) != null) {
903          VisualizePanel pan = new VisualizePanel();
904          pan.setInstances(inst);
905          JFrame nf = new JFrame();
906          nf.setSize(400, 300);
907          nf.getContentPane().add(pan);
908          nf.setVisible(true);
909        }
910        else {
911          JOptionPane.showMessageDialog(this, "Sorry, there is no " + 
912                                        "available Instances data for " +
913                                        "this Node.", "Sorry!",
914                                        JOptionPane.WARNING_MESSAGE); 
915        }
916      }
917      else {
918        JOptionPane.showMessageDialog(this, "Error, there is no " + 
919                                      "selected Node to perform " +
920                                      "this operation on.", "Error!",
921                                      JOptionPane.ERROR_MESSAGE); 
922      }
923    }
924    else if (e.getActionCommand().equals("Create Child Nodes")) {
925      if (m_focusNode >= 0) {
926        if (m_listener != null) {
927          //then send message to the listener
928          m_listener.userCommand(new TreeDisplayEvent
929            (TreeDisplayEvent.ADD_CHILDREN, 
930             m_nodes[m_focusNode].m_node.getRefer()));
931        }
932        else {
933          JOptionPane.showMessageDialog(this, "Sorry, there is no " + 
934                                        "available Decision Tree to " +
935                                        "perform this operation on.",
936                                        "Sorry!", 
937                                        JOptionPane.WARNING_MESSAGE);
938        }
939      }
940      else {
941        JOptionPane.showMessageDialog(this, "Error, there is no " +
942                                      "selected Node to perform this " +
943                                      "operation on.", "Error!",
944                                      JOptionPane.ERROR_MESSAGE);
945      }
946    }
947    else if (e.getActionCommand().equals("Remove Child Nodes")) {
948      if (m_focusNode >= 0) {
949        if (m_listener != null) {
950          //then send message to the listener
951          m_listener.userCommand(new 
952            TreeDisplayEvent(TreeDisplayEvent.REMOVE_CHILDREN, 
953                             m_nodes[m_focusNode].m_node.getRefer()));
954        }
955        else {
956          JOptionPane.showMessageDialog(this, "Sorry, there is no " + 
957                                        "available Decsion Tree to " +
958                                        "perform this operation on.",
959                                        "Sorry!", 
960                                        JOptionPane.WARNING_MESSAGE);
961        }
962      }
963      else {
964        JOptionPane.showMessageDialog(this, "Error, there is no " +
965                                      "selected Node to perform this " +
966                                      "operation on.", "Error!",
967                                      JOptionPane.ERROR_MESSAGE);
968      }
969    }
970    else if (e.getActionCommand().equals("classify_child")) {
971      if (m_focusNode >= 0) {
972        if (m_listener != null) {
973          //then send message to the listener
974          m_listener.userCommand(new TreeDisplayEvent
975            (TreeDisplayEvent.CLASSIFY_CHILD, 
976             m_nodes[m_focusNode].m_node.getRefer()));
977        }
978        else {
979          JOptionPane.showMessageDialog(this, "Sorry, there is no " + 
980                                        "available Decsion Tree to " +
981                                        "perform this operation on.",
982                                        "Sorry!", 
983                                        JOptionPane.WARNING_MESSAGE);
984        }
985      }
986      else {
987        JOptionPane.showMessageDialog(this, "Error, there is no " +
988                                      "selected Node to perform this " +
989                                      "operation on.", "Error!",
990                                      JOptionPane.ERROR_MESSAGE);
991      }
992    }
993    else if (e.getActionCommand().equals("send_instances")) {
994      if (m_focusNode >= 0) {
995        if (m_listener != null) {
996          //then send message to the listener
997          m_listener.userCommand(new TreeDisplayEvent
998            (TreeDisplayEvent.SEND_INSTANCES, 
999             m_nodes[m_focusNode].m_node.getRefer()));
1000        }
1001        else {
1002          JOptionPane.showMessageDialog(this, "Sorry, there is no " + 
1003                                        "available Decsion Tree to " +
1004                                        "perform this operation on.",
1005                                        "Sorry!", 
1006                                        JOptionPane.WARNING_MESSAGE);
1007        }
1008      }
1009      else {
1010        JOptionPane.showMessageDialog(this, "Error, there is no " +
1011                                      "selected Node to perform this " +
1012                                      "operation on.", "Error!",
1013                                      JOptionPane.ERROR_MESSAGE);
1014      }
1015    }
1016    else if (e.getActionCommand().equals("Accept The Tree")) {
1017      if (m_listener != null) {
1018        //then send message to the listener saying that the tree is done
1019        m_listener.userCommand(new TreeDisplayEvent(TreeDisplayEvent.ACCEPT,
1020                                                  null));
1021      }
1022      else {
1023        JOptionPane.showMessageDialog(this, "Sorry, there is no " +
1024                                      "available Decision Tree to " +
1025                                      "perform this operation on.",
1026                                      "Sorry!", 
1027                                      JOptionPane.WARNING_MESSAGE);
1028      }
1029    }
1030  }
1031
1032  /**
1033   * Performs the action associated with the ItemEvent.
1034   *
1035   * @param e the item event.
1036   */
1037  public void itemStateChanged(ItemEvent e)
1038  {
1039    JRadioButtonMenuItem c = (JRadioButtonMenuItem)e.getSource();
1040    if (c.getActionCommand().equals("Size 24")) {
1041      changeFontSize(24);
1042    }
1043    else if (c.getActionCommand().equals("Size 22")) {
1044      changeFontSize(22);
1045    }
1046    else if (c.getActionCommand().equals("Size 20")) {
1047      changeFontSize(20);
1048    }
1049    else if (c.getActionCommand().equals("Size 18")) {
1050      changeFontSize(18);
1051    } 
1052    else if (c.getActionCommand().equals("Size 16")) {
1053      changeFontSize(16);
1054    }
1055    else if (c.getActionCommand().equals("Size 14")) {
1056      changeFontSize(14);
1057    }
1058    else if (c.getActionCommand().equals("Size 12")) {
1059      changeFontSize(12);
1060    }
1061    else if (c.getActionCommand().equals("Size 10")) {
1062      changeFontSize(10);
1063    }
1064    else if (c.getActionCommand().equals("Size 8")) {
1065      changeFontSize(8);
1066    }
1067    else if (c.getActionCommand().equals("Size 6")) {
1068      changeFontSize(6);
1069    }
1070    else if (c.getActionCommand().equals("Size 4")) {
1071      changeFontSize(4);
1072    }
1073    else if (c.getActionCommand().equals("Size 2")) {
1074      changeFontSize(2);
1075    }
1076    else if (c.getActionCommand().equals("Size 1")) {
1077      changeFontSize(1);
1078    }
1079    else if (c.getActionCommand().equals("Hide Descendants")) {
1080      //focus_node.setCVisible(!c.isSelected());
1081      //no longer used...
1082    }
1083  }
1084
1085  /**
1086   * Does nothing.
1087   * @param e the mouse event.
1088   */
1089  public void mouseClicked(MouseEvent e) {
1090    //if the mouse was left clicked on
1091    //the node then
1092    if (m_clickAvailable) {
1093      //determine if the click was on a node or not
1094      int s = -1;
1095     
1096      for (int noa = 0; noa < m_numNodes;noa++) {
1097        if (m_nodes[noa].m_quad == 18) {
1098          //then is on the screen
1099          calcScreenCoords(noa);
1100          if (e.getX() <= m_nodes[noa].m_center + m_nodes[noa].m_side 
1101              && e.getX() 
1102              >= m_nodes[noa].m_center - m_nodes[noa].m_side &&
1103              e.getY() >= m_nodes[noa].m_top && e.getY() 
1104              <= m_nodes[noa].m_top + m_nodes[noa].m_height) {
1105            //then it is this node that the mouse was clicked on
1106            s = noa;
1107          }
1108          m_nodes[noa].m_top = 32000;
1109        }
1110      }
1111      m_focusNode = s;
1112     
1113      if (m_focusNode != -1) {
1114        if (m_listener != null) {
1115          //then set this to be the selected node for editing
1116          actionPerformed(new ActionEvent(this, 32000, "Create Child Nodes"));
1117         
1118        }
1119        else {
1120          //then open a visualize to display this nodes instances if possible
1121          actionPerformed(new ActionEvent(this, 32000, "Visualize The Node"));
1122        }
1123      }
1124    }
1125  }
1126 
1127  /**
1128   * Determines what action the user wants to perform.
1129   *
1130   * @param e the mouse event.
1131   */ 
1132  public void mousePressed(MouseEvent e) {
1133    m_frameLimiter.setRepeats(true);
1134    if ((e.getModifiers() & e.BUTTON1_MASK) != 0 && !e.isAltDown() && 
1135        m_mouseState == 0 
1136        && m_scaling == 0) {
1137      //then the left mouse button has been pressed
1138      //check for modifiers
1139     
1140      if (((e.getModifiers() & e.CTRL_MASK) != 0) && ((e.getModifiers() & e.SHIFT_MASK) == 0)) {
1141        //then is in zoom out mode
1142        m_mouseState = 2;
1143      }
1144      else if (((e.getModifiers() & e.SHIFT_MASK) != 0) && ((e.getModifiers() & e.CTRL_MASK) == 0)) {
1145        //then is in zoom mode
1146        //note if both are pressed default action is to zoom out
1147        m_oldMousePos.width = e.getX();
1148        m_oldMousePos.height = e.getY();
1149        m_newMousePos.width = e.getX();
1150        m_newMousePos.height = e.getY();
1151        m_mouseState = 3;
1152       
1153        Graphics g = getGraphics();
1154        if (m_ZoomBoxColor == null)
1155          g.setColor(Color.black);
1156        else
1157          g.setColor(m_ZoomBoxColor);
1158        if (m_ZoomBoxXORColor == null)
1159          g.setXORMode(Color.white);
1160        else
1161          g.setXORMode(m_ZoomBoxXORColor);
1162        g.drawRect(m_oldMousePos.width, m_oldMousePos.height,
1163                   m_newMousePos.width - m_oldMousePos.width, 
1164                   m_newMousePos.height - m_oldMousePos.height);
1165        g.dispose();
1166      }
1167      else {
1168        //no modifiers drag area around
1169        m_oldMousePos.width = e.getX();
1170        m_oldMousePos.height = e.getY();
1171        m_newMousePos.width = e.getX();
1172        m_newMousePos.height = e.getY();
1173        m_mouseState = 1;
1174        m_frameLimiter.start();
1175      }
1176     
1177    }
1178    // pop up save dialog explicitly (is somehow overridden...)
1179    else if ( (e.getButton() == MouseEvent.BUTTON1) && e.isAltDown() && e.isShiftDown() && !e.isControlDown() ) {
1180      saveComponent();
1181    }
1182    else if (m_mouseState == 0 && m_scaling == 0) {
1183      //either middle or right mouse button pushed
1184      //determine menu to use
1185     
1186    }
1187  }
1188 
1189  /**
1190   * Performs the final stages of what the user wants to perform.
1191   *
1192   * @param e the mouse event.
1193   */
1194  public void mouseReleased(MouseEvent e) {
1195    if (m_mouseState == 1) {
1196      //this is used by mouseClicked to determine if it is alright to do
1197      //something
1198      m_clickAvailable = true;
1199        //note that a standard click with the left mouse is pretty much the
1200      //only safe input left to be assigned anything.
1201    }
1202    else {
1203      m_clickAvailable = false;
1204    }
1205    if (m_mouseState == 2 && mouseInBounds(e)) {
1206      //then zoom out;
1207      m_mouseState = 0;
1208      Dimension ns = new Dimension(m_viewSize.width / 2, m_viewSize.height 
1209                                   / 2);
1210      if (ns.width < 10) {
1211        ns.width = 10;
1212      }
1213      if (ns.height < 10) {
1214        ns.height = 10;
1215      }
1216     
1217      Dimension d = getSize();
1218      Dimension np = new Dimension((int)(d.width / 2 
1219                                         - ((double)d.width / 2 
1220                                            - m_viewPos.width) / 2),
1221                                   (int)(d.height / 2 
1222                                         - ((double)d.height / 2
1223                                            - m_viewPos.height) / 2));
1224     
1225      animateScaling(np, ns, 10);
1226     
1227      //view_pos.width += view_size.width / 2;
1228      //view_pos.height += view_size.height / 2;
1229     
1230    }
1231    else if (m_mouseState == 3) {
1232      //then zoom in
1233      m_mouseState = 0;
1234      Graphics g = getGraphics();
1235      if (m_ZoomBoxColor == null)
1236        g.setColor(Color.black);
1237      else
1238        g.setColor(m_ZoomBoxColor);
1239      if (m_ZoomBoxXORColor == null)
1240        g.setXORMode(Color.white);
1241      else
1242        g.setXORMode(m_ZoomBoxXORColor);
1243      g.drawRect(m_oldMousePos.width, m_oldMousePos.height, 
1244                 m_newMousePos.width - m_oldMousePos.width, 
1245                 m_newMousePos.height - m_oldMousePos.height);
1246      g.dispose();
1247     
1248     
1249     
1250      int cw = m_newMousePos.width - m_oldMousePos.width;
1251      int ch = m_newMousePos.height - m_oldMousePos.height;
1252      if (cw >= 1 && ch >= 1) {
1253        if (mouseInBounds(e) && 
1254            (getSize().width / cw) <= 6 &&
1255            (getSize().height / ch) <= 6) {
1256         
1257          //now calculate new position and size
1258          Dimension ns = new Dimension();
1259          Dimension np = new Dimension();
1260          double nvsw = getSize().width / (double)(cw);
1261          double nvsh = getSize().height / (double)(ch);
1262          np.width = (int)((m_oldMousePos.width - m_viewPos.width) * -nvsw);
1263          np.height = (int)((m_oldMousePos.height - m_viewPos.height) * -nvsh);
1264          ns.width = (int)(m_viewSize.width * nvsw);
1265          ns.height = (int)(m_viewSize.height * nvsh);
1266         
1267          animateScaling(np, ns, 10);
1268         
1269         
1270        }
1271      }
1272    }
1273    else if (m_mouseState == 0 && m_scaling == 0) {
1274      //menu
1275      m_mouseState = 0;
1276      setFont(new Font("A Name", 0, 12));
1277      //determine if the click was on a node or not
1278      int s = -1;
1279     
1280      for (int noa = 0; noa < m_numNodes;noa++) {
1281        if (m_nodes[noa].m_quad == 18) {
1282          //then is on the screen
1283          calcScreenCoords(noa);
1284          if (e.getX() <= m_nodes[noa].m_center + m_nodes[noa].m_side 
1285              && e.getX() 
1286              >= m_nodes[noa].m_center - m_nodes[noa].m_side &&
1287              e.getY() >= m_nodes[noa].m_top && e.getY() 
1288              <= m_nodes[noa].m_top + m_nodes[noa].m_height) {
1289            //then it is this node that the mouse was clicked on
1290            s = noa;
1291          }
1292          m_nodes[noa].m_top = 32000;
1293        }
1294      }
1295      if (s == -1) {
1296        //the mouse wasn't clicked on a node
1297        m_winMenu.show(this,e.getX(),e.getY());
1298      }
1299      else {
1300        //the mouse was clicked on a node
1301        m_focusNode = s;
1302        m_nodeMenu.show(this, e.getX(), e.getY());
1303       
1304      }
1305      setFont(m_currentFont);
1306    }
1307    else if (m_mouseState == 1) {
1308      //dragging
1309      m_mouseState = 0;
1310      m_frameLimiter.stop();
1311      repaint();
1312    }
1313   
1314  }
1315 
1316  /**
1317   * Checks to see if the coordinates of the mouse lie on this JPanel.
1318   *
1319   * @param e the mouse event.
1320   * @return true if the mouse lies on this JPanel.
1321   */
1322  private boolean mouseInBounds(MouseEvent e) {
1323    //this returns true if the mouse is currently over the canvas otherwise
1324    //false
1325   
1326    if (e.getX() < 0 || e.getY() < 0 || e.getX() > getSize().width 
1327        || e.getY() > getSize().height) {
1328      return false;
1329    }
1330    return true;
1331  }
1332 
1333  /**
1334   * Performs intermediate updates to what the user wishes to do.
1335   *
1336   * @param e the mouse event.
1337   */
1338  public void mouseDragged(MouseEvent e) {
1339    //use mouse state to determine what to do to the view of the tree
1340   
1341    if (m_mouseState == 1) {
1342      //then dragging view
1343      m_oldMousePos.width = m_newMousePos.width;
1344      m_oldMousePos.height = m_newMousePos.height;
1345      m_newMousePos.width = e.getX();
1346      m_newMousePos.height = e.getY();
1347      m_viewPos.width += m_newMousePos.width - m_oldMousePos.width;
1348      m_viewPos.height += m_newMousePos.height - m_oldMousePos.height;
1349     
1350     
1351    }
1352    else if (m_mouseState == 3) {
1353      //then zoom box being created
1354      //redraw the zoom box
1355      Graphics g = getGraphics();
1356      if (m_ZoomBoxColor == null)
1357        g.setColor(Color.black);
1358      else
1359        g.setColor(m_ZoomBoxColor);
1360      if (m_ZoomBoxXORColor == null)
1361        g.setXORMode(Color.white);
1362      else
1363        g.setXORMode(m_ZoomBoxXORColor);
1364      g.drawRect(m_oldMousePos.width, m_oldMousePos.height,
1365                 m_newMousePos.width - m_oldMousePos.width, 
1366                 m_newMousePos.height - m_oldMousePos.height);
1367     
1368      m_newMousePos.width = e.getX();
1369      m_newMousePos.height = e.getY();
1370     
1371      g.drawRect(m_oldMousePos.width, m_oldMousePos.height,
1372                 m_newMousePos.width - m_oldMousePos.width, 
1373                 m_newMousePos.height - m_oldMousePos.height);
1374      g.dispose();
1375    }
1376   
1377   
1378  }
1379 
1380  /**
1381   * Does nothing.
1382   *
1383   * @param e the mouse event.
1384   */
1385  public void mouseMoved(MouseEvent e) {
1386  }
1387
1388  /**
1389   * Does nothing.
1390   *
1391   * @param e the mouse event.
1392   */
1393  public void mouseEntered(MouseEvent e) {
1394  }
1395 
1396  /**
1397   * Does nothing.
1398   *
1399   * @param e the mouse event.
1400   */
1401  public void mouseExited(MouseEvent e) {
1402  }
1403
1404  /**
1405   * Set the highlight for the node with the given id
1406   * @param id the id of the node to set the highlight for
1407   */
1408  public void setHighlight(String id) {
1409    //set the highlight for the node with the given id
1410   
1411    for (int noa = 0; noa < m_numNodes; noa++) {
1412      if (id.equals(m_nodes[noa].m_node.getRefer())) {
1413        //then highlight this node
1414        m_highlightNode = noa;
1415      }
1416    }
1417    //System.out.println("ahuh " + highlight_node + " " +
1418    //nodes[0].node.getRefer());
1419    repaint();
1420   
1421  }
1422 
1423  /**
1424   * Updates the screen contents.
1425   *
1426   * @param g the drawing surface.
1427   */
1428  public void paintComponent(Graphics g) {
1429    Color oldBackground = ((Graphics2D) g).getBackground();
1430    if (m_BackgroundColor != null)
1431      ((Graphics2D) g).setBackground(m_BackgroundColor);
1432    g.clearRect(0, 0, getSize().width, getSize().height);
1433    ((Graphics2D) g).setBackground(oldBackground);
1434    g.setClip(3, 7, getWidth() - 6, getHeight() - 10);
1435    painter(g);
1436    g.setClip(0, 0, getWidth(), getHeight());
1437   
1438  }
1439
1440  /**
1441   * Draws the tree to the graphics context
1442   *
1443   * @param g the drawing surface.
1444   */
1445  private void painter(Graphics g) {
1446    //I have moved what would normally be in the paintComponent
1447    //function to here
1448    //for now so that if I do in fact need to do double
1449    //buffering or the like it will be easier
1450
1451    //this will go through the table of edges and draw the edge if it deems the
1452    //two nodes attached to it could cause it to cut the screen or be on it.
1453   
1454    //in the process flagging all nodes so that they can quickly be put to the
1455    //screen if they lie on it
1456
1457    //I do it in this order because in some circumstances I have seen a line
1458    //cut through a node , to make things look better the line will
1459    //be drawn under the node
1460
1461
1462    //converting the screen edges to the node scale so that they
1463    //can be positioned relative to the screen
1464    //note I give a buffer around the edges of the screen.
1465   
1466    //when seeing
1467    //if a node is on screen I only bother to check the nodes top centre
1468    //if it has large enough size it may still fall onto the screen
1469    double left_clip = (double)(-m_viewPos.width - 50) / m_viewSize.width;
1470    double right_clip = (double)(getSize().width - m_viewPos.width + 50) / 
1471      m_viewSize.width;
1472    double top_clip = (double)(-m_viewPos.height - 50) / m_viewSize.height;
1473    double bottom_clip = (double)(getSize().height - m_viewPos.height + 50) / 
1474      m_viewSize.height;
1475 
1476
1477   
1478    //  12 10  9           //the quadrants
1479    //  20 18 17
1480    //  36 34 33
1481
1482
1483    //first the edges must be rendered
1484
1485    Edge e;
1486    Node r,s;
1487    double ncent,ntop;   
1488
1489    int row = 0, col = 0, pq, cq;
1490    for (int noa = 0 ; noa < m_numNodes ; noa++) {
1491      r = m_nodes[noa].m_node;
1492      if (m_nodes[noa].m_change) {
1493        //then recalc row component of quadrant
1494        ntop = r.getTop();
1495        if (ntop < top_clip) {
1496          row = 8;
1497        }
1498        else if (ntop > bottom_clip) {
1499          row = 32;
1500        }
1501        else {
1502          row = 16;
1503        }
1504      }
1505     
1506      //calc the column the node falls in for the quadrant
1507      ncent = r.getCenter();
1508      if (ncent < left_clip) {
1509        col = 4;
1510      }
1511      else if (ncent > right_clip) {
1512        col = 1;
1513      }
1514      else {
1515        col = 2;
1516      }
1517     
1518      m_nodes[noa].m_quad = row | col;
1519     
1520      if (m_nodes[noa].m_parent >= 0) {
1521        //this will draw the edge if it should be drawn
1522        //It will do this by eliminating all edges that definitely won't enter
1523        //the screen and then draw the rest
1524       
1525        pq = m_nodes[m_edges[m_nodes[noa].m_parent].m_parent].m_quad;
1526        cq = m_nodes[noa].m_quad;
1527       
1528        //note that this will need to be altered if more than 1 parent exists
1529        if ((cq & 8) == 8) {
1530          //then child exists above screen
1531        }
1532        else if ((pq & 32) == 32) {
1533          //then parent exists below screen
1534        }
1535        else if ((cq & 4) == 4 && (pq & 4) == 4) {
1536          //then both child and parent exist to the left of the screen
1537        }
1538        else if ((cq & 1) == 1 && (pq & 1) == 1) {
1539          //then both child and parent exist to the right of the screen
1540        }
1541        else {
1542          //then draw the line
1543          drawLine(m_nodes[noa].m_parent, g);
1544        }
1545      }
1546     
1547      //now draw the nodes
1548    }
1549   
1550    for (int noa = 0 ;noa < m_numNodes; noa++) {
1551      if (m_nodes[noa].m_quad == 18) {
1552        //then the node is on the screen , draw it
1553        drawNode(noa, g);
1554      }
1555    }
1556   
1557    if (m_highlightNode >= 0 && m_highlightNode < m_numNodes) {
1558      //then draw outline
1559      if (m_nodes[m_highlightNode].m_quad == 18) {
1560        Color acol;
1561        if (m_NodeColor == null)
1562          acol = m_nodes[m_highlightNode].m_node.getColor();
1563        else
1564          acol = m_NodeColor;
1565        g.setColor(new Color((acol.getRed() + 125) % 256, 
1566                             (acol.getGreen() + 125) % 256, 
1567                             (acol.getBlue() + 125) % 256));
1568        //g.setXORMode(Color.white);
1569        if (m_nodes[m_highlightNode].m_node.getShape() == 1) {
1570          g.drawRect(m_nodes[m_highlightNode].m_center 
1571                     - m_nodes[m_highlightNode].m_side,
1572                     m_nodes[m_highlightNode].m_top, 
1573                     m_nodes[m_highlightNode].m_width, 
1574                     m_nodes[m_highlightNode].m_height);
1575         
1576          g.drawRect(m_nodes[m_highlightNode].m_center 
1577                     - m_nodes[m_highlightNode].m_side 
1578                     + 1, m_nodes[m_highlightNode].m_top + 1,
1579                     m_nodes[m_highlightNode].m_width - 2, 
1580                     m_nodes[m_highlightNode].m_height - 2);
1581        }
1582        else if (m_nodes[m_highlightNode].m_node.getShape() == 2) {
1583          g.drawOval(m_nodes[m_highlightNode].m_center 
1584                     - m_nodes[m_highlightNode].m_side,
1585                     m_nodes[m_highlightNode].m_top, 
1586                     m_nodes[m_highlightNode].m_width, 
1587                     m_nodes[m_highlightNode].m_height);
1588         
1589          g.drawOval(m_nodes[m_highlightNode].m_center 
1590                     - m_nodes[m_highlightNode].m_side 
1591                     + 1, m_nodes[m_highlightNode].m_top + 1,
1592                     m_nodes[m_highlightNode].m_width - 2, 
1593                     m_nodes[m_highlightNode].m_height - 2);
1594        }
1595      }
1596    }
1597   
1598    for (int noa = 0;noa < m_numNodes;noa++) {
1599      //this resets the coords so that next time a refresh occurs
1600      //they don't accidentally get used
1601      //I will use 32000 to signify that they are invalid, even if this
1602      //coordinate occurs it doesn't
1603      //matter as it is only for the sake of the caching
1604     
1605      m_nodes[noa].m_top = 32000;
1606    }
1607  }
1608 
1609  /**
1610   * Determines the attributes of the node and draws it.
1611   *
1612   * @param n A subscript identifying the node in <i>nodes</i> array
1613   * @param g The drawing surface
1614   */
1615  private void drawNode(int n, Graphics g) {
1616    //this will draw a node and then print text on it
1617   
1618    if (m_NodeColor == null)
1619      g.setColor(m_nodes[n].m_node.getColor());
1620    else
1621      g.setColor(m_NodeColor);
1622    g.setPaintMode();
1623    calcScreenCoords(n);
1624    int x = m_nodes[n].m_center - m_nodes[n].m_side;
1625    int y = m_nodes[n].m_top;
1626    if (m_nodes[n].m_node.getShape() == 1) {
1627      g.fill3DRect(x, y, m_nodes[n].m_width, m_nodes[n].m_height, true);
1628      drawText(x, y, n, false, g);
1629     
1630    }
1631    else if (m_nodes[n].m_node.getShape() == 2) {
1632     
1633      g.fillOval(x, y, m_nodes[n].m_width, m_nodes[n].m_height);
1634      drawText(x, y + (int)(m_nodes[n].m_height * .15), n, false, g);
1635    }
1636  }
1637 
1638  /**
1639   * Determines the attributes of the edge and draws it.
1640   *
1641   * @param e A subscript identifying the edge in <i>edges</i> array.
1642   * @param g The drawing surface.
1643   */
1644  private void drawLine(int e, Graphics g) {
1645    //this will draw a line taking in the edge number and then getting
1646    //the nodes subscript for the parent and child entries
1647   
1648    //this will draw a line that has been broken in the middle
1649    //for the edge text to be displayed
1650    //if applicable
1651   
1652    //first convert both parent and child node coords to screen coords
1653    int p = m_edges[e].m_parent;
1654    int c = m_edges[e].m_child;
1655    calcScreenCoords(c);
1656    calcScreenCoords(p);
1657   
1658    if (m_LineColor == null)
1659      g.setColor(Color.black);
1660    else
1661      g.setColor(m_LineColor);
1662    g.setPaintMode();
1663   
1664    if (m_currentFont.getSize() < 2) {
1665      //text to small to bother cutting the edge
1666      g.drawLine(m_nodes[p].m_center, m_nodes[p].m_top + m_nodes[p].m_height, 
1667                 m_nodes[c].m_center, m_nodes[c].m_top); 
1668     
1669    }
1670    else {
1671      //find where to cut the edge to insert text
1672      int e_width = m_nodes[c].m_center - m_nodes[p].m_center;
1673      int e_height = m_nodes[c].m_top - (m_nodes[p].m_top 
1674                                         + m_nodes[p].m_height);
1675      int e_width2 = e_width / 2;
1676      int e_height2 = e_height / 2;
1677      int e_centerx = m_nodes[p].m_center + e_width2;
1678      int e_centery = m_nodes[p].m_top + m_nodes[p].m_height + e_height2;
1679      int e_offset = m_edges[e].m_tb;
1680     
1681      int tmp = (int)(((double)e_width / e_height) * 
1682                      (e_height2 - e_offset)) + m_nodes[p].m_center;
1683      //System.out.println(edges[e].m_height);
1684     
1685      //draw text now
1686     
1687      drawText(e_centerx - m_edges[e].m_side, e_centery - e_offset, e, true
1688               , g);
1689     
1690     
1691      if (tmp > (e_centerx - m_edges[e].m_side) && tmp
1692          < (e_centerx + m_edges[e].m_side)) {
1693        //then cut line on top and bottom of text
1694        g.drawLine(m_nodes[p].m_center, m_nodes[p].m_top + m_nodes[p].m_height
1695                   , tmp, e_centery - e_offset);  //first segment
1696        g.drawLine(e_centerx * 2 - tmp, e_centery + e_offset, 
1697                   m_nodes[c].m_center, m_nodes[c].m_top);    //second segment
1698      }
1699      else {
1700        e_offset = m_edges[e].m_side;
1701        if (e_width < 0) {
1702          e_offset *= -1;   //adjusting for direction which could otherwise
1703          //screw up the calculation
1704        }
1705        tmp = (int)(((double)e_height / e_width) * (e_width2 - e_offset)) + 
1706          m_nodes[p].m_top + m_nodes[p].m_height;
1707       
1708        g.drawLine(m_nodes[p].m_center, m_nodes[p].m_top + m_nodes[p].m_height
1709                   , e_centerx - e_offset, tmp);   //first segment
1710        g.drawLine(e_centerx + e_offset, e_centery * 2 - tmp, 
1711                   m_nodes[c].m_center, m_nodes[c].m_top);  //second segment
1712       
1713      }
1714    }
1715    //System.out.println("here" + nodes[p].center);
1716  }
1717
1718  /**
1719   * Draws the text for either an Edge or a Node.
1720   *
1721   * @param x1 the left side of the text area.
1722   * @param y1 the top of the text area.
1723   * @param s A subscript identifying either a Node or Edge.
1724   * @param e_or_n Distinguishes whether it is a node or edge.
1725   * @param g The drawing surface.
1726   */
1727  private void drawText(int x1, int y1, int s, boolean e_or_n, Graphics g) {
1728    //this function will take in the rectangle that the text should be
1729    //drawn in as well as the subscript
1730    //for either the edge or node and a boolean variable to tell which
1731   
1732    // backup color
1733    Color oldColor = g.getColor();
1734
1735    g.setPaintMode();
1736    if (m_FontColor == null)
1737      g.setColor(Color.black);
1738    else
1739      g.setColor(m_FontColor);
1740    String st;
1741    if (e_or_n) {
1742      //then paint for edge
1743      Edge e = m_edges[s].m_edge;
1744      for (int noa = 0;(st = e.getLine(noa)) != null; noa++) {
1745        g.drawString(st, (m_edges[s].m_width - m_fontSize.stringWidth(st)) / 2 
1746                     + x1,
1747                     y1 + (noa + 1) * m_fontSize.getHeight()); 
1748      }
1749    }
1750    else {
1751      //then paint for node
1752      Node e = m_nodes[s].m_node;
1753      for (int noa = 0;(st = e.getLine(noa)) != null; noa++) {
1754        g.drawString(st, (m_nodes[s].m_width - m_fontSize.stringWidth(st)) / 2 
1755                     + x1,
1756                     y1 + (noa + 1) * m_fontSize.getHeight()); 
1757      }
1758    }
1759   
1760    // restore color
1761    g.setColor(oldColor);
1762  }
1763 
1764  /**
1765   * Converts the internal coordinates of the node found from <i>n</i>
1766   * and converts them to the actual screen coordinates.
1767   *
1768   * @param n A subscript identifying the Node.
1769   */
1770  private void calcScreenCoords(int n) {
1771    //this converts the coordinate system the Node uses into screen coordinates
1772    // System.out.println(n + " " + view_pos.height + " " +
1773    //nodes[n].node.getCenter());
1774    if (m_nodes[n].m_top == 32000) {
1775      m_nodes[n].m_top = ((int)(m_nodes[n].m_node.getTop() 
1776                                * m_viewSize.height)) + m_viewPos.height;
1777      m_nodes[n].m_center = ((int)(m_nodes[n].m_node.getCenter() 
1778                                   * m_viewSize.width)) + m_viewPos.width;
1779    }
1780  }
1781
1782  /**
1783   * This Calculates the minimum size of the tree which will prevent any text
1784   * overlapping and make it readable, and then set the size of the tree to
1785   * this.
1786   */
1787  private void autoScale() {
1788    //this function will determine the smallest scale value that keeps the text
1789    //from overlapping
1790    //it will leave the view centered
1791   
1792    int dist;
1793    Node ln,rn;
1794    Dimension temp = new Dimension(10, 10);
1795   
1796    if (m_numNodes <= 1) {
1797      return;
1798    }
1799   
1800    //calc height needed by first node
1801    dist = (m_nodes[0].m_height + 40) * m_numLevels;
1802    if (dist > temp.height) {
1803      temp.height = dist;
1804    }
1805   
1806    for (int noa = 0;noa < m_numNodes - 1;noa++) {
1807      calcScreenCoords(noa); 
1808      calcScreenCoords(noa+1);
1809      if (m_nodes[noa+1].m_change) {
1810        //then on a new level so don't check width this time round
1811      }
1812      else {
1813       
1814        dist = m_nodes[noa+1].m_center - m_nodes[noa].m_center; 
1815        //the distance between the node centers, along horiz
1816        if (dist <= 0) {
1817          dist = 1;
1818        }
1819        dist = ((6 + m_nodes[noa].m_side + m_nodes[noa+1].m_side) 
1820                * m_viewSize.width) / dist; //calc optimal size for width
1821       
1822        if (dist > temp.width) {
1823         
1824          temp.width = dist;
1825        }
1826      }
1827      //now calc.. minimun hieght needed by nodes
1828     
1829      dist = (m_nodes[noa+1].m_height + 40) * m_numLevels;
1830      if (dist > temp.height) {
1831       
1832        temp.height = dist;
1833      }
1834    }
1835   
1836    int y1, y2, xa, xb;
1837   
1838    y1 = m_nodes[m_edges[0].m_parent].m_top;
1839    y2 = m_nodes[m_edges[0].m_child].m_top;
1840   
1841    dist = y2 - y1;
1842    if (dist <= 0) {
1843      dist = 1;
1844    }
1845    dist = ((60 + m_edges[0].m_height + m_nodes[m_edges[0].m_parent].m_height) 
1846            * m_viewSize.height) / dist;
1847    if (dist > temp.height) {
1848     
1849      temp.height = dist;
1850    }
1851   
1852    for (int noa = 0;noa < m_numNodes - 2; noa++) {
1853      //check the edges now
1854      if (m_nodes[m_edges[noa+1].m_child].m_change) {
1855        //then edge is on a different level , so skip this one
1856      }
1857      else {
1858        //calc the width requirements of this pair of edges
1859       
1860        xa = m_nodes[m_edges[noa].m_child].m_center 
1861          - m_nodes[m_edges[noa].m_parent].m_center;
1862        xa /= 2;
1863        xa += m_nodes[m_edges[noa].m_parent].m_center;
1864       
1865        xb = m_nodes[m_edges[noa+1].m_child].m_center - 
1866          m_nodes[m_edges[noa+1].m_parent].m_center;
1867        xb /= 2;
1868        xb += m_nodes[m_edges[noa+1].m_parent].m_center;
1869       
1870        dist = xb - xa;
1871        if (dist <= 0) {
1872          dist = 1;
1873        }
1874        dist = ((12 + m_edges[noa].m_side + m_edges[noa+1].m_side) 
1875                * m_viewSize.width) 
1876          / dist;
1877        if (dist > temp.width) {
1878         
1879          temp.width = dist;
1880        }
1881      }
1882      //now calc height need by the edges
1883      y1 = m_nodes[m_edges[noa+1].m_parent].m_top;
1884      y2 = m_nodes[m_edges[noa+1].m_child].m_top;
1885     
1886      dist = y2 - y1;
1887      if (dist <= 0) {
1888       
1889        dist = 1;
1890      }
1891      dist = ((60 + m_edges[noa+1].m_height 
1892               + m_nodes[m_edges[noa+1].m_parent].m_height) 
1893              * m_viewSize.height) / dist;
1894     
1895      if (dist > temp.height) {
1896       
1897        temp.height = dist;
1898      }
1899    }
1900
1901    Dimension e = getSize();
1902   
1903    Dimension np = new Dimension();
1904    np.width = (int)(e.width / 2 -  (((double)e.width / 2) - m_viewPos.width) /
1905                     ((double)m_viewSize.width) * (double)temp.width);
1906    np.height = (int)(e.height / 2 -  (((double)e.height / 2) - 
1907                                       m_viewPos.height) /       
1908                      ((double)m_viewSize.height) * (double)temp.height);
1909    //animate_scaling(c_size,c_pos,25);
1910   
1911    for (int noa = 0;noa < m_numNodes;noa++) {
1912      //this resets the coords so that next time a refresh occurs they don't
1913      //accidentally get used
1914      //I will use 32000 to signify that they are invalid, even if this
1915      //coordinate occurs it doesn't
1916      //matter as it is only for the sake of the caching
1917     
1918      m_nodes[noa].m_top = 32000;
1919     
1920    }
1921    animateScaling(np, temp, 10);
1922  }
1923
1924  /**
1925   * This will increment the size and position of the tree towards the
1926   * desired size and position
1927   * a little (depending on the value of <i>frames</i>) everytime it is called.
1928   *
1929   * @param n_pos The final position of the tree wanted.
1930   * @param n_size The final size of the tree wanted.
1931   * @param frames The number of frames that shall occur before the final
1932   * size and pos is reached.
1933   */
1934  private void animateScaling(Dimension n_pos,Dimension n_size,int frames) {
1935    //this function will take new size and position coords , and incrementally
1936    //scale the view to these
1937    //since I will be tying it in with the framelimiter I will simply call
1938    //this function and increment it once
1939    //I will have to use a global variable since I am doing it proportionally
1940
1941    if (frames == 0) {
1942      System.out.println("the timer didn't end in time");
1943      m_scaling = 0;
1944    }
1945    else {
1946      if (m_scaling == 0) {
1947        //new animate session
1948        //start timer and set scaling
1949        m_frameLimiter.start();
1950        m_nViewPos.width = n_pos.width;
1951        m_nViewPos.height = n_pos.height;
1952        m_nViewSize.width = n_size.width;
1953        m_nViewSize.height = n_size.height;
1954       
1955        m_scaling = frames;
1956      }
1957     
1958      int s_w = (n_size.width - m_viewSize.width) / frames;
1959      int s_h = (n_size.height - m_viewSize.height) / frames;
1960      int p_w = (n_pos.width - m_viewPos.width) / frames;
1961      int p_h = (n_pos.height - m_viewPos.height) / frames;
1962     
1963      m_viewSize.width += s_w;
1964      m_viewSize.height += s_h;
1965     
1966      m_viewPos.width += p_w;
1967      m_viewPos.height += p_h;
1968     
1969      repaint();
1970     
1971      m_scaling--;
1972      if (m_scaling == 0) {
1973        //all done
1974        m_frameLimiter.stop();
1975      }
1976    }
1977  }
1978 
1979  /**
1980   * This will change the font size for displaying the tree to the one
1981   * specified.
1982   *
1983   * @param s The new pointsize of the font.
1984   */
1985  private void changeFontSize(int s) {
1986    //this will set up the new font that shall be used
1987    //it will also recalculate the size of the nodes as these will change as
1988    //a result of
1989    //the new font size
1990    setFont(m_currentFont = new Font("A Name", 0, s));             
1991
1992    m_fontSize = getFontMetrics(getFont());
1993   
1994    Dimension d;
1995
1996    for (int noa = 0; noa < m_numNodes; noa++) {
1997      //this will set the size info for each node and edge
1998     
1999      d = m_nodes[noa].m_node.stringSize(m_fontSize);
2000     
2001      if (m_nodes[noa].m_node.getShape() == 1) {
2002        m_nodes[noa].m_height = d.height + 10;
2003        m_nodes[noa].m_width = d.width + 8;
2004        m_nodes[noa].m_side = m_nodes[noa].m_width / 2;
2005      }
2006      else if (m_nodes[noa].m_node.getShape() == 2) {
2007        m_nodes[noa].m_height = (int)((d.height + 2) * 1.6);
2008        m_nodes[noa].m_width = (int)((d.width + 2) * 1.6);
2009        m_nodes[noa].m_side = m_nodes[noa].m_width / 2;
2010      }
2011     
2012      if (noa < m_numNodes - 1) {
2013        //this will do the same for edges
2014       
2015        d = m_edges[noa].m_edge.stringSize(m_fontSize);
2016       
2017        m_edges[noa].m_height =  d.height + 8;
2018        m_edges[noa].m_width = d.width + 8;
2019        m_edges[noa].m_side = m_edges[noa].m_width / 2;
2020        m_edges[noa].m_tb = m_edges[noa].m_height / 2;
2021      }
2022    }
2023  }
2024
2025  /**
2026   * This will fill two arrays with the Nodes and Edges from the tree
2027   * into a particular order.
2028   *
2029   * @param t The top Node of the tree.
2030   * @param l An array that has already been allocated, to be filled.
2031   * @param k An array that has already been allocated, to be filled.
2032   */
2033  private void arrayFill(Node t, NodeInfo[] l, EdgeInfo[] k) {
2034   
2035    //this will take the top node and the array to fill
2036    //it will go through the tree structure and and fill the array with the
2037    //nodes
2038    //from top to bottom left to right
2039   
2040    //note I do not believe this function will be able to deal with multiple
2041    //parents
2042
2043    if (t == null || l == null) {
2044      System.exit(1);      //this is just a preliminary safety check
2045      //(i shouldn' need it)
2046    }
2047   
2048    Edge e;
2049    Node r,s;
2050    l[0] = new NodeInfo();
2051    l[0].m_node = t;
2052    l[0].m_parent = -1;
2053    l[0].m_change = true;
2054   
2055    int floater;       //this will point at a node that has previously been
2056    //put in the list
2057    //all of the children that this node has shall be put in the list ,
2058    //once this is done the floater shall point at the next node in the list
2059    //this will allow the nodes to be put into order from closest to top node
2060    //to furtherest from top node
2061
2062    int free_space = 1; //the next empty array position
2063
2064    double height = t.getTop(); //this will be used to determine if the node
2065    //has a
2066    //new height compared to the
2067    //previous one
2068
2069    for (floater = 0;floater < free_space;floater++) {
2070      r = l[floater].m_node;
2071      for (int noa = 0;(e = r.getChild(noa)) != null;noa++) {
2072        //this loop pulls out each child of r
2073       
2074        //e points to a child edge, getTarget will return that edges child node
2075        s = e.getTarget();
2076        l[free_space] = new NodeInfo();
2077        l[free_space].m_node = s;
2078        l[free_space].m_parent = free_space - 1;
2079       
2080        k[free_space - 1] = new EdgeInfo();
2081        k[free_space - 1].m_edge = e;
2082        k[free_space - 1].m_parent = floater;
2083        k[free_space - 1].m_child = free_space;     //note although it's child
2084        //will always have a subscript
2085        //of 1 more , I may not nessecarily have access to that
2086        //and it will need the subscr.. for multiple parents
2087       
2088        //determine if level of node has changed from previous one
2089        if (height != s.getTop()) {
2090          l[free_space].m_change = true;
2091          height = s.getTop();
2092        }
2093        else {
2094          l[free_space].m_change = false;
2095        }
2096        free_space++;
2097      }
2098    }
2099  }
2100
2101  /**
2102   * Internal Class for containing display information about a Node.
2103   */
2104  private class NodeInfo {
2105    //this class contains a pointer to the node itself along with extra
2106    //information
2107    //about the node used by the Displayer class
2108
2109    /** The y pos of the node on screen. */
2110    int m_top = 32000;           //the main node coords calculated out
2111
2112    /** The x pos of the node on screen. */
2113    int m_center;        // these coords will probably change each refresh
2114
2115    //and are the positioning coords
2116    //which the rest of the offsets use
2117   
2118    /** The offset to get to the left or right of the node. */
2119    int m_side;          //these are the screen offset for the dimensions of
2120
2121    //the node relative to the nodes
2122    //internal top and center values (after they have been converted to
2123    //screen coords
2124    /** The width of the node. */
2125    int m_width;
2126
2127    /** The height of the node. */
2128    int m_height;
2129
2130    /** True if the node is at the start (left) of a new level (not sibling
2131     * group). */
2132    boolean m_change;    //this is quickly used to identify whether the node
2133    //has chenged height from the
2134    //previous one to help speed up the calculation of what row it lies in
2135
2136    /** The subscript number of the Nodes parent. */
2137    int m_parent;     //this is the index of the nodes parent edge in an array
2138
2139    /** The rough position of the node relative to the screen. */
2140    int m_quad;       //what of nine quadrants is it in
2141
2142    /*
2143      12 10  9
2144      20 18 17          //18 being the screen
2145      36 34 33          //this arrangement uses 6 bits, each bit represents a
2146      row or column
2147    */
2148
2149    /** The Node itself. */
2150    Node m_node;
2151  }
2152
2153  /**
2154   * Internal Class for containing display information about an Edge.
2155   */
2156  private class EdgeInfo {
2157    //this class contains a pointer to the edge along with all the other
2158    //extra info about the edge
2159   
2160    /** The parent subscript (for a Node). */
2161    int m_parent;            //array indexs for its two connections
2162
2163    /** The child subscript (for a Node). */
2164    int m_child;
2165   
2166
2167    /** The distance from the center of the text to either side. */
2168    int m_side;            //these are used to describe the dimensions of the
2169    //text
2170
2171    /** The distance from the center of the text to top or bottom. */
2172    int m_tb;              //tb stands for top , bottom, this is simply the
2173    //distance from the middle to top bottom
2174
2175    /** The width of the text. */
2176    int m_width;
2177
2178    /** The height of the text. */
2179    int m_height;
2180
2181    /** The Edge itself. */
2182    Edge m_edge;
2183  }
2184
2185  /**
2186   * Main method for testing this class.
2187   * @param args first argument should be the name of a file that contains
2188   * a tree discription in dot format.
2189   */
2190  public static void main(String[] args) {
2191    try {
2192      weka.core.logging.Logger.log(weka.core.logging.Logger.Level.INFO, "Logging started");
2193      //put in the random data generator right here
2194      // this call with import java.lang gives me between 0 and 1 Math.random
2195      TreeBuild builder = new TreeBuild();
2196      Node top = null;
2197      NodePlace arrange = new PlaceNode2();
2198      //top = builder.create(new StringReader("digraph atree { top [label=\"the top\"] a [label=\"the first node\"] b [label=\"the second nodes\"] c [label=\"comes off of first\"] top->a top->b b->c }"));
2199      top = builder.create(new FileReader(args[0]));
2200
2201      int num = top.getCount(top,0);
2202      //System.out.println("counter counted " + num + " nodes");
2203      //System.out.println("there are " + num + " nodes");
2204      TreeVisualizer a = new TreeVisualizer(null, top, arrange);
2205      a.setSize(800 ,600);
2206      //a.setTree(top);
2207      JFrame f;
2208      f = new JFrame();
2209      //a.addMouseMotionListener(a);
2210      //a.addMouseListener(a);
2211      //f.add(a);
2212      Container contentPane = f.getContentPane();
2213      contentPane.add(a);
2214      f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
2215      f.setSize(800,600);
2216      f.setVisible(true);
2217      //f.
2218      //find_prop(top);
2219      //a.setTree(top,arrange);//,(num + 1000), num / 2 + 1000);
2220    }
2221    catch(IOException e) {
2222      // ignored
2223    }
2224  }
2225}
Note: See TracBrowser for help on using the repository browser.