SyntaxTreeViewer.java
0001 /*
0002  *  SyntaxTreeViewer.java
0003  *
0004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
0005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0006  *
0007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
0008  *  software, licenced under the GNU Library General Public License,
0009  *  Version 2, June 1991 (in the distribution as file licence.html,
0010  *  and also available at http://gate.ac.uk/gate/licence.html).
0011  *
0012  *  Kalina Bontcheva, 20/09/2000
0013  *
0014  *  $Id: SyntaxTreeViewer.java 17616 2014-03-10 16:09:07Z markagreenwood $
0015  */
0016 
0017 package gate.gui;
0018 
0019 //java imports
0020 import gate.Annotation;
0021 import gate.AnnotationSet;
0022 import gate.CreoleRegister;
0023 import gate.Document;
0024 import gate.Gate;
0025 import gate.LanguageResource;
0026 import gate.Node;
0027 import gate.creole.ANNIEConstants;
0028 import gate.creole.AbstractVisualResource;
0029 import gate.creole.AnnotationSchema;
0030 import gate.creole.AnnotationVisualResource;
0031 import gate.creole.FeatureSchema;
0032 import gate.util.Coordinates;
0033 import gate.util.Err;
0034 import gate.util.GateException;
0035 import gate.util.InvalidOffsetException;
0036 import gate.util.Out;
0037 
0038 import java.awt.BorderLayout;
0039 import java.awt.Color;
0040 import java.awt.Dimension;
0041 import java.awt.Graphics;
0042 import java.awt.Insets;
0043 import java.awt.Rectangle;
0044 import java.awt.event.ActionEvent;
0045 import java.awt.event.ActionListener;
0046 import java.awt.event.ComponentEvent;
0047 import java.awt.event.KeyEvent;
0048 import java.awt.event.MouseEvent;
0049 import java.awt.event.MouseListener;
0050 import java.beans.PropertyChangeEvent;
0051 import java.util.ArrayList;
0052 import java.util.Collections;
0053 import java.util.Enumeration;
0054 import java.util.HashMap;
0055 import java.util.HashSet;
0056 import java.util.Iterator;
0057 import java.util.Map;
0058 import java.util.Set;
0059 import java.util.Vector;
0060 
0061 import javax.swing.Icon;
0062 import javax.swing.JButton;
0063 import javax.swing.JMenuItem;
0064 import javax.swing.JOptionPane;
0065 import javax.swing.JPopupMenu;
0066 import javax.swing.Scrollable;
0067 import javax.swing.SwingConstants;
0068 import javax.swing.SwingUtilities;
0069 
0070 
0071 /**
0072   * The SyntaxTreeViewer is capable of showing and editing utterances (fancy
0073   * name for sentences) and the
0074   * attached syntax trees. It works by taking an utterance and all Token
0075   * annotations and constructs the text. Then it also gets all SyntaxTreeNode
0076   * annotations and builds and shows the syntac tree for that utterance. The
0077   * leaves of the tree are the tokens, which constitute the utterance.<P>
0078   *
0079   * It is possible to configure the annotation types that are used by the
0080   * viewer. The textAnnotationType property specifies the type
0081   * of annotation which is used to denote the utterance (sentence).
0082   * In GATE, the value of this property is not set directly, but is derived
0083   * from the VR configuration information from creole.xml (explained below).
0084   *
0085   * The treeNodeAnnotationType is the name of the
0086   * annotations which encode the SyntaxTreeNodes; default - SyntaxTreeNode.
0087   * To change when part of GATE, modify the <PARAMETER> setting of the
0088   * TreeViewer entry in creole.xml. Similarly, one can change which annotation
0089   * is used for chunking the utterance. By default, it is Token, which is also
0090   * specified in creole.xml as a parameter in the treeviewer entry.
0091   *
0092   * The component assumes that the annotations of type treeNodeAnnotationType have
0093   * features called: cat with a value String; consists which is a List either
0094   * empty or with annotation ids of the node's children; and optionally
0095   * text which contains
0096   * the text covered by this annotation. The component will work fine even
0097   * without the last feature. Still when it creates annotations,
0098   * these will have this feature added. <P>
0099   *
0100   *
0101   * Newly added tree nodes to the tree are added to the document
0102   * as annotations and deleted nodes are automatically deleted from the document
0103   * only after OK is chosen in the dialog. Cancel does not make any changes
0104   * permanent. <P>
0105   *
0106   * Configuring the viewer in GATE<P>
0107   * The viewer is configured in creole.xml. The default entry is:
0108   <PRE>
0109   *   <RESOURCE>
0110   *     <NAME>Syntax tree viewer</NAME>
0111   *     <CLASS>gate.gui.SyntaxTreeViewer</CLASS>
0112   *     <!-- type values can be  "large" or "small"-->
0113   *     <GUI>
0114   *       <MAIN_VIEWER/>
0115   *       <ANNOTATION_TYPE_DISPLAYED>Sentence</ANNOTATION_TYPE_DISPLAYED>
0116   *       <PARAMETER NAME="treeNodeAnnotationType" DEFAULT="SyntaxTreeNode"
0117   *                  RUNTIME="false" OPTIONAL="true">java.lang.String
0118   *       </PARAMETER>
0119   *       <PARAMETER NAME="tokenType" DEFAULT="Token" RUNTIME="false"
0120   *                  OPTIONAL="true">java.lang.String
0121   *       </PARAMETER>
0122   *     </GUI>
0123   *   </RESOURCE>
0124   </PRE>
0125   *
0126   * The categories that appear in the menu for manual annotation are determined
0127   * from SyntaxTreeViewerSchema.xml. If you want to change the default set,
0128   * you must edit this file and update your Gate jar accordingly (e.g., by
0129   * recompilation. This does not affect the categories of SyntaxTreeNode
0130   * annotations, which have been created automatically by some other process,
0131   * e.g., a parser PR.
0132   *
0133   <P>
0134   * If used outside GATE,
0135   * in order to have appropriate behaviour always put this component inside a
0136   * scroll pane or something similar that provides scrollers.
0137   * Example code: <BREAK>
0138   <PRE>
0139   *  JScrollPane scroller = new JScrollPane(syntaxTreeViewer1);
0140   *  scroller.setPreferredSize(syntaxTreeViewer1.getPreferredSize());
0141   *  frame.getContentPane().add(scroller, BorderLayout.CENTER);
0142   </PRE>
0143   *
0144   *
0145   * The default way is to pass just one annotation of type textAnnotationType
0146   * which corresponds to the entire sentence or utterance to be annotated with
0147   * syntax tree information. Then the viewer automatically tokenises it
0148   * (by obtaining the relevant token annotations) and creates the leaves.<P>
0149   *
0150   * To create a new annotation, use setSpan, instead of setAnnotation.
0151   *
0152   <P> In either
0153   * case, you must call setTarget first, because that'll provide the viewer
0154   * with the document's annotation set, from where it can obtain the token
0155   * annotations.
0156   <P> If you intend to use the viewer outside GATE and do not understand
0157   * the API, e-mail gate@dcs.shef.ac.uk.
0158   */
0159 
0160 @SuppressWarnings("serial")
0161 public class SyntaxTreeViewer extends AbstractVisualResource
0162     implements  Scrollable, ActionListener, MouseListener,
0163                 AnnotationVisualResource {
0164 
0165   /** The annotation type used to encode each tree node*/
0166   public static final String TREE_NODE_ANNOTATION_TYPE = "SyntaxTreeNode";
0167   /** The name of the feature that encodes the tree node's category information */
0168   public static final String NODE_CAT_FEATURE_NAME = "cat";
0169   /** The name of the feature that encodes the subtree annotations */
0170   public static final String NODE_CONSISTS_FEATURE_NAME = "consists";
0171 
0172   // class members
0173   // whether to use any layout or not
0174   protected boolean laidOut = false;
0175 
0176   // display all buttons x pixels apart horizontally
0177   protected int horizButtonGap = 5;
0178 
0179   // display buttons at diff layers x pixels apart vertically
0180   protected int vertButtonGap = 50;
0181 
0182   // extra width in pixels to be added to each button
0183   protected int extraButtonWidth = 10;
0184 
0185   // number of pixels to be used as increment by scroller
0186   protected int maxUnitIncrement = 10;
0187 
0188   // GUI members
0189   BorderLayout borderLayout1 = new BorderLayout();
0190   JPopupMenu popup = new JPopupMenu()//the right-click popup
0191   Color buttonBackground;
0192   Color selectedNodeColor = Color.red.darker();
0193 
0194   // the HashSet with the coordinates of the lines to draw
0195   Set<Coordinates> lines = new HashSet<Coordinates>();
0196 
0197   // The utterance to be annotated as a sentence. It's not used if the tree
0198   // is passed
0199   // as annotations.
0200   protected Annotation utterance;
0201   protected Long utteranceStartOffset = new Long(0);
0202   protected Long utteranceEndOffset = new Long(0);
0203   protected AnnotationSet currentSet = null;
0204 
0205   protected String tokenType = ANNIEConstants.TOKEN_ANNOTATION_TYPE;
0206 
0207   // for internal use only. Set when the utterance is set.
0208   protected String displayedString = "";
0209 
0210   // The name of the annotation type which is used to locate the
0211   // stereotype with the allowed categories
0212   // also when reading and creating annotations
0213   protected String treeNodeAnnotationType = TREE_NODE_ANNOTATION_TYPE;
0214 
0215   // The annotation name of the annotations used to extract the
0216   // text that appears at the leaves of the tree. For now the viewer
0217   // supports only one such annotation but might be an idea to extend it
0218   // so that it gets its text off many token annotations, which do not
0219   // need to be tokenised or off the syntax tree annotations themselves.
0220   protected String textAnnotationType = ANNIEConstants.SENTENCE_ANNOTATION_TYPE;
0221 
0222   // all leaf nodes
0223   protected HashMap<Integer, STreeNode> leaves = new HashMap<Integer, STreeNode>();
0224 
0225   // all non-terminal nodes
0226   protected HashMap<Integer, STreeNode> nonTerminals = new HashMap<Integer, STreeNode>();
0227 
0228   // all buttons corresponding to any node
0229   protected HashMap<Integer, JButton> buttons = new HashMap<Integer, JButton>();
0230 
0231   // all selected buttons
0232   protected Vector<JButton> selection = new Vector<JButton>();
0233 
0234   // all annotations to be displayed
0235   protected AnnotationSet treeAnnotations;
0236 
0237   protected Document document = null;
0238   // the document to which the annotations belong
0239 
0240   //true when a new utterance annotation has been added
0241   //then if the user presses cancel, I need to delete it
0242   protected boolean utteranceAdded = false;
0243 
0244 
0245   public SyntaxTreeViewer() {
0246     try  {
0247       jbInit();
0248     }
0249     catch(Exception ex) {
0250       ex.printStackTrace(Err.getPrintWriter());
0251     }
0252 
0253   }
0254 
0255   //CONSTRUCTORS
0256   @SuppressWarnings("unused")
0257   private SyntaxTreeViewer(String annotType) {
0258 
0259     treeNodeAnnotationType = annotType;
0260     try  {
0261       jbInit();
0262     }
0263     catch(Exception ex) {
0264       ex.printStackTrace(Err.getPrintWriter());
0265     }
0266   }
0267 
0268   //METHODS
0269   private void jbInit() throws Exception {
0270 
0271     //check if we're using a layout; preferrably not
0272     if (laidOut)
0273       this.setLayout(borderLayout1);
0274     else
0275       this.setLayout(null);
0276 
0277     this.setPreferredSize(new Dimension (600400));
0278     this.setSize(600400);
0279     this.setBounds(00600400);
0280     this.addComponentListener(new java.awt.event.ComponentAdapter() {
0281       @Override
0282       public void componentShown(ComponentEvent e) {
0283         this_componentShown(e);
0284       }
0285       @Override
0286       public void componentHidden(ComponentEvent e) {
0287         this_componentHidden(e);
0288       }
0289     });
0290     this.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
0291 
0292       @Override
0293       public void propertyChange(PropertyChangeEvent e) {
0294         this_propertyChange(e);
0295       }
0296     });
0297 
0298     buttonBackground = Color.red; //this.getBackground();
0299 
0300     //get all categories from stereotype
0301     fillCategoriesMenu();
0302 
0303     //initialise the popup menu
0304 
0305     //add popup to container
0306     this.add(popup);
0307   }// private void jbInit()
0308 
0309   // Methods required by AnnotationVisualResource
0310 
0311   /**
0312     * Used when the viewer/editor has to display/edit an existing annotation
0313     @param ann the annotation to be displayed or edited. If ann is null then
0314     * the method simply returns
0315     */
0316   @Override
0317   public void editAnnotation(Annotation ann, AnnotationSet set){
0318     if (ann == null || set == nullreturn;
0319 
0320     utterance = ann;
0321     currentSet = set;
0322     document = set.getDocument();
0323     utteranceStartOffset = utterance.getStartNode().getOffset();
0324     utteranceEndOffset = utterance.getEndNode().getOffset();
0325     textAnnotationType = ann.getType();
0326 
0327     clearAll();
0328     utterances2Trees();
0329     annotations2Trees();
0330     this.setVisible(true);
0331     repaint();
0332   }
0333 
0334   /**
0335    * Called by the GUI when the user has pressed the "OK" button. This should
0336    * trigger the saving of the newly created annotation(s)
0337    */
0338   @Override
0339   public void okAction() throws GateException{
0340     //Out.println("Visible coords" + this.getVisibleRect().toString());
0341     //Out.println("Size" + this.getBounds().toString());
0342     STreeNode.transferAnnotations(document, currentSet);
0343 
0344   //okAction()
0345 
0346   /**
0347    * Called by the GUI when the user has pressed the "Cancel" button. This should
0348    * trigger the cleanup operation
0349    */
0350   @Override
0351   public void cancelAction() throws GateException{
0352     //if we added a new utterance but user does not want it any more...
0353     if (utteranceAdded) {
0354       currentSet.remove(utterance)//delete it
0355       utteranceAdded = false;
0356     }
0357     //also cleanup the temporary annotation sets used by the viewer
0358     //to cache the added and deleted tree annotations
0359     STreeNode.undo(document);
0360 
0361   //cancelAction()
0362 
0363   /**
0364    * Returns <tt>true</tt>.
0365    */
0366   @Override
0367   public boolean supportsCancel() {
0368     return true;
0369   }
0370 
0371   
0372   /**
0373    * Returns <tt>true</tt>
0374    */
0375   @Override
0376   public boolean editingFinished() {
0377     return true;
0378   }
0379 
0380   /* (non-Javadoc)
0381    * @see gate.creole.AnnotationVisualResource#getAnnotationCurrentlyEdited()
0382    */
0383   @Override
0384   public Annotation getAnnotationCurrentlyEdited() {
0385     return utterance;
0386   }
0387 
0388   /* (non-Javadoc)
0389    * @see gate.creole.AnnotationVisualResource#getAnnotationSetCurrentlyEdited()
0390    */
0391   @Override
0392   public AnnotationSet getAnnotationSetCurrentlyEdited() {
0393     return currentSet;
0394   }
0395 
0396   /* (non-Javadoc)
0397    * @see gate.creole.AnnotationVisualResource#isActive()
0398    */
0399   @Override
0400   public boolean isActive() {
0401     return isVisible();
0402   }
0403 
0404   /**
0405     * Checks whether this viewer/editor can handle a specific annotation type.
0406     @param annotationType represents the annotation type being questioned.If
0407     * it is <b>null</b> then the method will return false.
0408     @return true if the SchemaAnnotationEditor can handle the annotationType
0409     * or false otherwise.
0410     */
0411   @Override
0412   public boolean canDisplayAnnotationType(String annotationType){
0413     // Returns true only if the there is an AnnotationSchema with the same type
0414     // as annotationType.
0415     if (annotationType == nullreturn false;
0416     boolean found = false;
0417 
0418     java.util.List<String> specificEditors = Gate.getCreoleRegister().
0419                                      getAnnotationVRs(annotationType);
0420     Iterator<String> editorIter = specificEditors.iterator();
0421     while(editorIter.hasNext() && !found){
0422       String editorClass = editorIter.next();
0423 
0424 //      Out.println(editorClass);
0425       if (editorClass.equals(this.getClass().getCanonicalName())) {
0426         textAnnotationType = annotationType;
0427         found = true;
0428       }
0429     }
0430 
0431     return found;
0432   }// canDisplayAnnotationType();
0433 
0434 
0435 /*  public static void main(String[] args) throws Exception {
0436     Gate.init();
0437     // final String text = "This is a sentence. That is another one.";
0438     final String text = "\u0915\u0932\u094d\u0907\u0928\u0643\u0637\u0628 \u041a\u0430\u043b\u0438\u043d\u0430 Kalina";
0439     final Document doc = Factory.newDocument(text);
0440 
0441     // that works too but only use if you have the test file there.
0442     // final Document doc = Factory.newDocument(
0443     //                        new URL("file:///z:/temp/weird.txt"), "UTF-8");
0444 
0445 
0446     final SyntaxTreeViewer syntaxTreeViewer1 =
0447       new SyntaxTreeViewer("SyntaxTreeNode");
0448     //syntaxTreeViewer1.setUnicodeSupportEnabled(true);
0449     //need to set the document here!!!!
0450 
0451 
0452     JFrame frame = new JFrame();
0453 
0454     //INITIALISE THE FRAME, ETC.
0455     frame.setEnabled(true);
0456     frame.setTitle("SyntaxTree Viewer");
0457     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
0458 
0459     // frame.getContentPane().add(syntaxTreeViewer1, BorderLayout.CENTER);
0460     // intercept the closing event to shut the application
0461     frame.addWindowListener(new WindowAdapter() {
0462       public void windowClosing(WindowEvent e) {
0463         AnnotationSet hs = doc.getAnnotations().get("SyntaxTreeNode");
0464         if (hs != null && hs.size() > 0) {
0465           int k = 0;
0466           for (Iterator i = hs.iterator(); i.hasNext(); k++) {
0467             Out.println("Tree Annot " + k + ": ");
0468             Out.println(i.next().toString());
0469           }
0470         } //if
0471         Out.println("Exiting...");
0472         //System.exit(0);
0473       }
0474     });
0475 
0476     //Put the bean in a scroll pane.
0477     JScrollPane scroller = new JScrollPane(syntaxTreeViewer1);
0478     scroller.setPreferredSize(syntaxTreeViewer1.getPreferredSize());
0479     frame.getContentPane().add(scroller, BorderLayout.CENTER);
0480 
0481     //DISPLAY FRAME
0482     frame.pack();
0483     frame.show();
0484 
0485     FeatureMap attrs = Factory.newFeatureMap();
0486     attrs.put("time", new Long(0));
0487     attrs.put("text", doc.getContent().toString());
0488 */
0489     /*
0490     FeatureMap attrs1 = Factory.newFeatureMap();
0491     attrs1.put("cat", "N");
0492     attrs1.put("text", "This");
0493     attrs1.put("consists", new Vector());
0494 
0495     FeatureMap attrs2 = Factory.newFeatureMap();
0496     attrs2.put("cat", "V");
0497     attrs2.put("text", "is");
0498     attrs2.put("consists", new Vector());
0499     */
0500 
0501 /*
0502     doc.getAnnotations().add( new Long(0), new Long(
0503                       doc.getContent().toString().length()),"utterance", attrs);
0504 */
0505     /* Integer id1 = doc.getAnnotations().add(new Long(0), new Long(4),
0506                               "SyntaxTreeNode", attrs1);
0507     Integer id2 = doc.getAnnotations().add(new Long(5), new Long(7),
0508                               "SyntaxTreeNode", attrs2);
0509 
0510     FeatureMap attrs3 = Factory.newFeatureMap();
0511     attrs3.put("cat", "VP");
0512     attrs3.put("text", "This is");
0513     Vector consists = new Vector();
0514     consists.add(id1);
0515     consists.add(id2);
0516     attrs3.put("consists", consists);
0517     doc.getAnnotations().add(new Long(0), new Long(7),
0518                                                   "SyntaxTreeNode", attrs3);
0519     */
0520 
0521 /*
0522     HashSet set = new HashSet();
0523     set.add("utterance");
0524     set.add("SyntaxTreeNode");
0525     AnnotationSet annots = doc.getAnnotations().get(set);
0526     syntaxTreeViewer1.setTreeAnnotations(annots);
0527 
0528   }// public static void main
0529 */
0530 
0531   @Override
0532   protected void paintComponent(Graphics g) {
0533     super.paintComponentg);
0534     drawLines(g);
0535   }// protected void paintComponent(Graphics g)
0536 
0537 
0538   private void drawLines(Graphics g) {
0539 
0540     for (Iterator<Coordinates> i = lines.iterator(); i.hasNext()) {
0541       Coordinates coords = i.next();
0542 
0543       g.drawLinecoords.getX1(),
0544                   coords.getY1(),
0545                   coords.getX2(),
0546                   coords.getY2());
0547     }// for
0548   }// private void drawLines(Graphics g)
0549 
0550   @Override
0551   public Dimension getPreferredScrollableViewportSize() {
0552         return getPreferredSize();
0553   }// public Dimension getPreferredScrollableViewportSize()
0554 
0555   @Override
0556   public int getScrollableUnitIncrement(Rectangle visibleRect,
0557                                               int orientation, int direction) {
0558     return maxUnitIncrement;
0559   }// public int getScrollableUnitIncrement
0560 
0561   @Override
0562   public int getScrollableBlockIncrement(Rectangle visibleRect,
0563                                               int orientation, int direction) {
0564     if (orientation == SwingConstants.HORIZONTAL)
0565         return visibleRect.width - maxUnitIncrement;
0566     else
0567         return visibleRect.height - maxUnitIncrement;
0568   }// public int getScrollableBlockIncrement
0569 
0570   @Override
0571   public boolean getScrollableTracksViewportWidth() {
0572     return false;
0573   }// public boolean getScrollableTracksViewportWidth()
0574 
0575   @Override
0576   public boolean getScrollableTracksViewportHeight() {
0577     return false;
0578   }
0579 
0580   void this_propertyChange(PropertyChangeEvent e) {
0581 
0582     //we have a new utterance to display and annotate
0583     if (e.getPropertyName().equals("utterance")) {
0584       clearAll();
0585       utterances2Trees();
0586     }
0587 
0588   //this_propertyChange
0589 
0590   /**
0591     * Clear all buttons and tree nodes created because component is being
0592     * re-initialised. Not sure it works perfectly.
0593     */
0594   private void clearAll() {
0595     lines.clear();
0596     this.removeAll();
0597     buttons.clear();
0598     leaves.clear();
0599     nonTerminals.clear();
0600   }
0601 
0602   /**
0603     * Converts the annotations into treeNodes
0604     */
0605   private void annotations2Trees() {
0606     if (document == nullreturn;
0607 
0608     Map<Integer, JButton> processed = new HashMap<Integer, JButton>()//for all processed annotations
0609 
0610     //first get all tree nodes in this set, then restrict them by offset
0611     AnnotationSet tempSet = currentSet.get(treeNodeAnnotationType);
0612     if (tempSet == null || tempSet.isEmpty())
0613       return;
0614     treeAnnotations = tempSet.get(utterance.getStartNode().getOffset(),
0615                                   utterance.getEndNode().getOffset());
0616     if (treeAnnotations == null || treeAnnotations.isEmpty())
0617       return;
0618 
0619     // sort them from left to right first
0620     // Should work as
0621     // annotation implements Comparable
0622     java.util.List<Annotation> nodeAnnots = new ArrayList<Annotation>(treeAnnotations);
0623     Collections.sort(nodeAnnots, new gate.util.OffsetComparator());
0624 
0625     //find all annotations with no children
0626     Iterator<Annotation> i = nodeAnnots.iterator();
0627     while (i.hasNext()) {
0628       Annotation annot = i.next();
0629 
0630       @SuppressWarnings("unchecked")
0631       java.util.List<Integer> children =
0632         (java.util.List<Integer>annot.getFeatures().get(NODE_CONSISTS_FEATURE_NAME);
0633       //check if it's a leaf
0634       if (children == null ||
0635           children.isEmpty())
0636         {
0637 
0638         STreeNode leaf = findLeaf(annot.getStartNode(), annot.getEndNode());
0639         if (leaf == null) {//not found
0640           throw new NullPointerException("Can't find leaf node for annotation: " + annot);
0641         }
0642 
0643         JButton button = buttons.get(new Integer(leaf.getID()));
0644         selection.clear();
0645         selection.add(button);
0646 
0647         //then create the non-terminal with the category
0648         STreeNode node = new STreeNode(annot);
0649         node.add(leaf);
0650         node.setLevel(1);
0651         node.setUserObject(annot.getFeatures().get(NODE_CAT_FEATURE_NAME));
0652         nonTerminals.put(new Integer(node.getID()), node);
0653         JButton parentButton = createCentralButton(node);
0654         addLines(node);
0655 
0656         //finally add to the processed annotations
0657         processed.put(annot.getId(), parentButton);
0658 
0659       //if
0660 
0661     //loop through children
0662 
0663     //loop through the rest of the nodes
0664     Iterator<Annotation> i1 = nodeAnnots.iterator();
0665     while (i1.hasNext()) {
0666       Annotation annotNode = i1.next();
0667       if (processed.containsKey(annotNode.getId()))
0668         continue;
0669       processChildrenAnnots(annotNode, processed);
0670     //process all higher nodes
0671 
0672     selection.clear();
0673 
0674     this.scrollRectToVisible(new
0675       Rectangle(0, getHeight()(intgetVisibleRect().getHeight(),
0676         (intgetVisibleRect().getWidth()(intgetVisibleRect().getHeight()));
0677   //annotations2Trees
0678 
0679   @SuppressWarnings("unchecked")
0680   private JButton processChildrenAnnots(Annotation annot, Map<Integer, JButton> processed) {
0681     selection.clear();
0682     Vector<JButton> childrenButtons = new Vector<JButton>();
0683 
0684     java.util.List<Integer> children =
0685       (java.util.List<Integer>annot.getFeatures().get(NODE_CONSISTS_FEATURE_NAME);
0686 
0687     for (Iterator<Integer> i = children.iterator(); i.hasNext()) {
0688       Integer childId = i.next();
0689       Annotation child = treeAnnotations.get(childId);
0690       JButton childButton;
0691 
0692       if (processed.containsKey(child.getId()))
0693         childButton = processed.get(child.getId());
0694       else
0695         childButton = processChildrenAnnots(child, processed);
0696 
0697       childrenButtons.add(childButton);
0698     }
0699 
0700     selection = (Vector<JButton>childrenButtons.clone();
0701     STreeNode parent = createParentNode(
0702                           (Stringannot.getFeatures().get(NODE_CAT_FEATURE_NAME),
0703                           annot);
0704     nonTerminals.put(new Integer(parent.getID()), parent);
0705     JButton parentButton = createCentralButton(parent);
0706     addLines(parent);
0707 
0708     processed.put(annot.getId(), parentButton);
0709     selection.clear();
0710     return parentButton;
0711   }// private JButton processChildrenAnnots
0712 
0713   private STreeNode findLeaf(Node start, Node end) {
0714     for (Iterator<STreeNode> i = leaves.values().iterator(); i.hasNext()) {
0715       STreeNode node = i.next();
0716       if (node.getStart() == start.getOffset().intValue() &&
0717           node.getEnd() == end.getOffset().intValue()
0718          )
0719         return node;
0720     }
0721 
0722     return null;
0723   }//private STreeNode findLeaf(Node start, Node end)
0724 
0725 
0726   /**
0727     * Converts the given utterances into a set of leaf nodes for annotation
0728     */
0729   private void utterances2Trees() {
0730 
0731     if (! utterance.getType().equals(textAnnotationType)) {
0732       Out.println("Can't display annotations other than the specified type:" +
0733                                                             textAnnotationType);
0734       return;
0735     }
0736 
0737     // set the utterance offset correctly.
0738     // All substring calculations depend on that.
0739     utteranceStartOffset = utterance.getStartNode().getOffset();
0740     utteranceEndOffset = utterance.getEndNode().getOffset();
0741 
0742     try {
0743       displayedString = currentSet.getDocument().getContent().getContent(
0744                         utteranceStartOffset, utteranceEndOffset).toString();
0745     catch (InvalidOffsetException ioe) {
0746       ioe.printStackTrace(Err.getPrintWriter());
0747     }
0748 
0749     AnnotationSet tokensAS = currentSet.get(tokenType, utteranceStartOffset,
0750                                           utteranceEndOffset);
0751     if (tokensAS == null || tokensAS.isEmpty()) {
0752       Out.println("TreeViewer warning: No annotations of type " + tokenType +
0753                   "so cannot show or edit the text and the tree annotations.");
0754       return;
0755     }
0756 
0757     Insets insets = this.getInsets();
0758     // the starting X position for the buttons
0759     int buttonX = insets.left;
0760 
0761     // the starting Y position
0762     int buttonY = this.getHeight() 20 - insets.bottom;
0763 
0764     java.util.List<Annotation> tokens = new ArrayList<Annotation>(tokensAS);
0765     //if no tokens to match, do nothing
0766     if (tokens.isEmpty())
0767        return;
0768     Collections.sort(tokens, new gate.util.OffsetComparator());
0769 
0770     //loop through the tokens
0771     for (int i= 0; i< tokens.size(); i++) {
0772       Annotation tokenAnnot = tokens.get(i);
0773       Long tokenBegin = tokenAnnot.getStartNode().getOffset();
0774       Long tokenEnd = tokenAnnot.getEndNode().getOffset();
0775 
0776       String tokenText = "";
0777       try {
0778         tokenText = document.getContent().getContent(
0779                         tokenBegin, tokenEnd).toString();
0780       catch (InvalidOffsetException ioe) {
0781         ioe.printStackTrace(Err.getPrintWriter());
0782       }
0783 
0784       // create the leaf node
0785       STreeNode node =
0786         new STreeNode(tokenBegin.longValue(), tokenEnd.longValue());
0787 
0788       // make it a leaf
0789       node.setAllowsChildren(false);
0790 
0791       // set the text
0792       node.setUserObject(tokenText);
0793       node.setLevel(0);
0794 
0795       // add to hash table of leaves
0796       leaves.put(new Integer(node.getID()), node);
0797 
0798       // create the corresponding button
0799       buttonX = createButton4Node(node, buttonX, buttonY);
0800 
0801     //while
0802 
0803 
0804 /*
0805     //This old piece of code was used to tokenise, instead of relying on
0806     // annotations. Can re-instate if someone shows me the need for it.
0807 
0808     long currentOffset = utteranceStartOffset.longValue();
0809 
0810     StrTokeniser strTok =
0811         new StrTokeniser(displayedString,
0812                         " \r\n\t");
0813 
0814     Insets insets = this.getInsets();
0815     // the starting X position for the buttons
0816     int buttonX = insets.left;
0817 
0818     // the starting Y position
0819     int buttonY = this.getHeight() - 20 - insets.bottom;
0820 
0821     while (strTok.hasMoreTokens()) {
0822       String word = strTok.nextToken();
0823 //      Out.println("To display:" + word);
0824 
0825       // create the leaf node
0826       STreeNode node =
0827         new STreeNode(currentOffset, currentOffset + word.length());
0828 
0829       // make it a leaf
0830       node.setAllowsChildren(false);
0831 
0832       // set the text
0833       node.setUserObject(word);
0834       node.setLevel(0);
0835 
0836       // add to hash table of leaves
0837       leaves.put(new Integer(node.getID()), node);
0838 
0839       // create the corresponding button
0840       buttonX = createButton4Node(node, buttonX, buttonY);
0841 
0842       currentOffset += word.length()+1;  //// +1 to include the delimiter too
0843     }
0844 */
0845 
0846     this.setSize(buttonX, buttonY + 20 + insets.bottom);
0847     // this.resize(buttonX, buttonY + 20 + insets.bottom);
0848     this.setPreferredSize(this.getSize());
0849 
0850   // utterance2Trees
0851 
0852   /**
0853     * Returns the X position where another button can start if necessary.
0854     * To be used to layout only the leaf buttons. All others must be created
0855     * central to their children using createCentralButton.
0856     */
0857   private int createButton4Node(STreeNode node, int buttonX, int buttonY) {
0858 
0859     JButton button = new JButton((Stringnode.getUserObject());
0860     button.setBorderPainted(false);
0861     button.setMargin(new Insets(0,0,0,0));
0862 
0863 //    FontMetrics fm = button.getFontMetrics(button.getFont());
0864 
0865     
0866 //    int buttonWidth,
0867 //        buttonHeight;
0868 
0869     // Out.print
0870     //  ("Button width " + b1.getWidth() + "Button height " + b1.getHeight());
0871 
0872 //    buttonWidth = fm.stringWidth(button.getText())
0873 //                  + button.getMargin().left + button.getMargin().right
0874 //                  + extraButtonWidth;
0875 //    buttonHeight = fm.getHeight() + button.getMargin().top +
0876 //                      button.getMargin().bottom;
0877     
0878 //    buttonWidth = buttonSize.width;
0879 //    buttonHeight = buttonSize.height;
0880     
0881 //    buttonY = buttonY - buttonHeight;
0882 
0883 //     Out.print("New Button X " + buttonX +
0884 //        "New Button Y" + buttonY);
0885 
0886 //    button.setBounds(buttonX, buttonY, buttonWidth, buttonHeight);
0887     
0888     Dimension buttonSize = button.getPreferredSize();
0889     button.setSize(buttonSize);
0890     buttonY = buttonY - buttonSize.height;
0891     button.setLocation(buttonX, buttonY);
0892     button.addActionListener(this);
0893     button.addMouseListener(this);
0894     button.setActionCommand("" + node.getID());
0895     button.setVisible(true);
0896     button.setEnabled(true);
0897 
0898     this.add(button);
0899     buttons.put(new Integer(node.getID()), button);
0900 
0901     buttonX +=  buttonSize.width + horizButtonGap;
0902     return buttonX;
0903 
0904   }// private int createButton4Node(STreeNode node, int buttonX, int buttonY)
0905 
0906   private JButton createCentralButton(STreeNode newNode) {
0907 
0908     FocusButton button = new FocusButton((StringnewNode.getUserObject());
0909     button.setBorderPainted(false);
0910 
0911 //    FontMetrics fm = button.getFontMetrics(button.getFont());
0912 
0913     int buttonWidth,
0914         buttonHeight,
0915         buttonX = 0,
0916         buttonY =0;
0917 
0918     // Out.print("Button width " + b1.getWidth() + ";
0919     //    Button height " + b1.getHeight());
0920 
0921     Dimension buttonSize = button.getPreferredSize();
0922     
0923 //    buttonWidth = fm.stringWidth(button.getText())
0924 //                  + button.getMargin().left + button.getMargin().right
0925 //                  + extraButtonWidth;
0926 //    buttonHeight = fm.getHeight() + button.getMargin().top +
0927 //                      button.getMargin().bottom;
0928 
0929     buttonWidth = buttonSize.width;
0930     buttonHeight = buttonSize.height;
0931     
0932     int left = this.getWidth(), right =, top = this.getHeight();
0933 
0934     // determine the left, right, top
0935     for (Iterator<JButton> i = selection.iterator(); i.hasNext()) {
0936       JButton childButton = i.next();
0937 
0938       if (left > childButton.getX())
0939         left = childButton.getX();
0940       if (childButton.getX() + childButton.getWidth() > right)
0941         right = childButton.getX() + childButton.getWidth();
0942       if (childButton.getY() < top)
0943         top = childButton.getY();
0944     }
0945 
0946     buttonX = (left + right/- buttonWidth/2;
0947     buttonY = top - vertButtonGap;
0948     // Out.println("Button's Y is" + buttonY);
0949 
0950     // Out.print("New Button width " + buttonWidth + ";
0951     //    New Button height " + buttonHeight);
0952     button.setBounds(buttonX, buttonY, buttonWidth, buttonHeight);
0953     button.addActionListener(this);
0954     button.addMouseListener(this);
0955     // button.registerKeyboardAction(this,
0956     //                          "delete",
0957     //                           KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
0958     //                           WHEN_FOCUSED);
0959 
0960     button.setActionCommand("" + newNode.getID());
0961 
0962     this.add(button);
0963     // add to hashmap of buttons
0964     buttons.put(new Integer(newNode.getID()), button);
0965 
0966     // check if we need to resize the panel
0967     if (buttonY < 0) {
0968       this.setSize(this.getWidth()this.getHeight() 5(- buttonY));
0969       this.setPreferredSize(this.getSize());
0970       shiftButtonsDown(5(-buttonY));
0971     }
0972 
0973     return button;
0974   }// private JButton createCentralButton(STreeNode newNode)
0975 
0976   private void shiftButtonsDown(int offset) {
0977     for (Iterator<JButton> i = buttons.values().iterator(); i.hasNext()) {
0978       JButton button = i.next();
0979       button.setBounds(    button.getX(),
0980                           button.getY() + offset,
0981                           button.getWidth(),
0982                           button.getHeight());
0983     // for loop through buttons
0984 
0985     for (Iterator<Coordinates> k = lines.iterator(); k.hasNext()) {
0986       Coordinates coords = k.next();
0987       coords.setY1(coords.getY1() + offset);
0988       coords.setY2(coords.getY2() + offset);
0989     }
0990   }// private void shiftButtonsDown(int offset)
0991 
0992   @Override
0993   public void actionPerformed(ActionEvent e) {
0994 
0995     //check for the popup menu items
0996     if (e.getSource() instanceof JMenuItem) {
0997       JMenuItem menuItem = (JMenuIteme.getSource();
0998 
0999       // check if we're annotating a leaf
1000       // the popup label is set to leaves when the popup has been
1001       // constructed in showRightClickPopup
1002       if (popup.getLabel().equals("leaves")) {
1003         Integer id = new Integer(e.getActionCommand());
1004 
1005 //        clearSelection();
1006         JButton button = buttons.get(id);
1007         selection.add(button);
1008 
1009         STreeNode leaf = leaves.get(id);
1010 
1011         // create parent with the same span as leaf
1012         // using createParentNode here is not a good idea coz it works only
1013         // for creating parents of non-terminal nodes, not leaves
1014         STreeNode parent = new STreeNode(leaf.getStart(), leaf.getEnd());
1015         parent.setLevel(leaf.getLevel()+1)//levels increase from bottom to top
1016         parent.add(leaf);
1017 
1018         // set the text
1019         parent.setUserObject(menuItem.getText());
1020 
1021         // last create the annotation; should always come last!
1022         parent.createAnnotation(  document,
1023                                   treeNodeAnnotationType,
1024                                   displayedString,
1025                                   utteranceStartOffset.longValue());
1026         nonTerminals.put(new Integer(parent.getID()), parent);
1027 
1028         // create new button positioned centrally above the leaf
1029         createCentralButton(parent);
1030 
1031         // add the necessary lines for drawing
1032         addLines(parent);
1033 
1034         clearSelection();
1035 
1036         // repaint the picture!
1037         this.repaint();
1038       // finished processing leaves
1039       else if (popup.getLabel().equals("non-terminal")) {
1040         // the action command is set to the id under which
1041         // the button can be found
1042         Integer id = new Integer(e.getActionCommand());
1043 
1044         //locate button from buttons hashMap and add to selection
1045         JButton button = buttons.get(id);
1046         selection.add(button);
1047 
1048         //create the new parent
1049         STreeNode parent = createParentNode(menuItem.getText());
1050 
1051         //add to nonTerminals HashMap
1052         nonTerminals.put(new Integer(parent.getID()), parent);
1053 
1054         //create new button positioned centrally above the leaf
1055         createCentralButton(parent);
1056 
1057         //add the necessary lines for drawing
1058         addLines(parent);
1059 
1060         clearSelection();
1061 
1062         //repaint the picture!
1063         this.repaint();
1064 
1065       //check for non-terminals
1066 
1067     //if statement for MenuItems
1068 
1069 
1070   }// public void actionPerformed(ActionEvent e)
1071 
1072   @Override
1073   public void mouseClicked(MouseEvent e) {
1074 
1075     if ((e.getSource() instanceof JButton))
1076       return;
1077 
1078     JButton source = (JButtone.getSource();
1079 
1080     //check if CTRL or Shift is pressed and if not, clear the selection
1081     if (((e.isControlDown() || e.isShiftDown()))
1082          && SwingUtilities.isLeftMouseButton(e))
1083       clearSelection();
1084 
1085     //and select the current node
1086     if (SwingUtilities.isLeftMouseButton(e))
1087     //if (e.getModifiers() == e.BUTTON1_MASK)
1088       selectNode(e);
1089 
1090 
1091     //only repspond to right-clicks here by displaying the popup
1092     if (SwingUtilities.isRightMouseButton(e)) {
1093       //if button not in focus, grad the focus and select it!
1094       if source.getBackground() != selectedNodeColor ) {
1095         source.grabFocus();
1096         source.doClick();
1097         selectNode(e);
1098       }
1099       //Out.println(e.getComponent().getClass() + " right-clicked!");
1100       showRightClickPopup(e);
1101     //end of right-click processing
1102 
1103   }// public void mouseClicked(MouseEvent e)
1104 
1105   @Override
1106   public void mousePressed(MouseEvent e) {
1107   }
1108 
1109   @Override
1110   public void mouseReleased(MouseEvent e) {
1111   }
1112 
1113   @Override
1114   public void mouseEntered(MouseEvent e) {
1115   }
1116 
1117   @Override
1118   public void mouseExited(MouseEvent e) {
1119   // createButton4Node
1120 
1121 
1122   private void showRightClickPopup(MouseEvent e) {
1123 
1124     //that'll always work coz we checked it in MouseClicked.
1125     JButton source = (JButtone.getSource();
1126     Integer id = new Integer(source.getActionCommand());
1127 
1128     //check if it's a leaf and if so, offer the leaf annotation dialog
1129     Object obj = leaves.get(id);
1130     if (obj != null) {
1131       STreeNode leaf = (STreeNodeobj;
1132       //do nothing if it already has a parent
1133       if (leaf.getParent() != null) {
1134         clearSelection();
1135         JOptionPane.showMessageDialog(
1136           this,
1137           "Node already annotated. To delete the existing annotation, " +
1138           "select it and press <DEL>.",
1139           "Syntax Tree Viewer message",
1140           JOptionPane.INFORMATION_MESSAGE);
1141         return;
1142       }
1143 
1144       //reset the popup and set it's heading accordingly
1145       popup.setLabel("leaves");
1146       setMenuCommands(popup, ""+id);
1147 
1148       popup.pack();
1149       popup.show(source, e.getX(), e.getY());
1150     else //we have a non-terminal node
1151 
1152       //check if it has been annotated already
1153       if nonTerminals.get(id).getParent() != null) {
1154         clearSelection();
1155         JOptionPane.showMessageDialog(this, "Node already annotated. To delete"+
1156                           " the existing annotation, select it and press <DEL>.",
1157                           "Syntax Tree Viewer message",
1158                           JOptionPane.INFORMATION_MESSAGE);
1159         return;  //and do nothing if so!
1160       }
1161 
1162       popup.setLabel("non-terminal");
1163       setMenuCommands(popup, ""+id);
1164 
1165       popup.pack();
1166       popup.show(source, e.getX(), e.getY());
1167 
1168     }
1169 
1170   //showRightClickPopup
1171 
1172   private void addLines(STreeNode newNode) {
1173 
1174     JButton newButton = buttons.get(new Integer(newNode.getID()));
1175     int nbX = newButton.getX() + newButton.getWidth()/2;
1176     int nbY = newButton.getY() + newButton.getHeight();
1177 
1178     for (Iterator<JButton> i = selection.iterator(); i.hasNext()) {
1179       JButton selButton = i.next();
1180 
1181       //I create it a rect but it will in fact be used as x1, y1, x2, y2 for the
1182       //draw line. see drawLines.
1183       Coordinates coords = new Coordinates(
1184                                 nbX,
1185                                 nbY,
1186                                 selButton.getX() + selButton.getWidth()/2,
1187                                 selButton.getY());
1188 
1189       lines.add(coords);
1190     }
1191 
1192   // addLines
1193 
1194   private void clearSelection() {
1195     for (Enumeration<JButton> enumeration = selection.elements(); enumeration.hasMoreElements()) {
1196       JButton selButton = enumeration.nextElement();
1197       selButton.setBackground(buttonBackground);
1198     }
1199 
1200     selection.clear();
1201 
1202   //clearSlection
1203 
1204 
1205   private void fillCategoriesMenu() {
1206     boolean found = false;
1207 
1208     //fetch the valid categories from the stereotype
1209     CreoleRegister creoleReg = Gate.getCreoleRegister();
1210     java.util.List<LanguageResource> currentAnnotationSchemaList =
1211                       creoleReg.getLrInstances("gate.creole.AnnotationSchema");
1212     if (currentAnnotationSchemaList.isEmpty()) return;
1213 
1214     Iterator<LanguageResource> iter = currentAnnotationSchemaList.iterator();
1215     while (iter.hasNext()){
1216       AnnotationSchema annotSchema = (AnnotationSchemaiter.next();
1217       //we have found the right schema
1218       if (treeNodeAnnotationType.equals(annotSchema.getAnnotationName())) {
1219         found = true;
1220         FeatureSchema categories = annotSchema.getFeatureSchema(NODE_CAT_FEATURE_NAME);
1221         //iterate through all categories
1222         for (Iterator<Object> i =
1223                 categories.getPermittedValues().iterator(); i.hasNext()) {
1224 
1225           JMenuItem menuItem = new JMenuItem( (Stringi.next() );
1226           menuItem.addActionListener(this);
1227           popup.add(menuItem);
1228         //for
1229 
1230       //if
1231     }// while
1232 
1233     //if we don't have a schema, issue a warning
1234     if (! found)
1235       Out.println("Warning: You need to define an annotation schema for " +
1236                   treeNodeAnnotationType +
1237                   " in order to be able to add such annotations.");
1238 
1239   // fillCategoriesMenu
1240 
1241   /**
1242     * Sets the action commands of all menu items to the specified command
1243     */
1244   private void setMenuCommands(JPopupMenu menu, String command) {
1245     for (int i = 0; i < menu.getComponentCount() ; i++) {
1246       JMenuItem item = (JMenuItemmenu.getComponent(i);
1247       item.setActionCommand(command);
1248     }
1249 
1250   // setMenuCommands
1251 
1252   /**
1253     * Create a parent node for all selected non-terminal nodes
1254     */
1255   protected STreeNode createParentNode(String text) {
1256     STreeNode  parentNode = new STreeNode();
1257 
1258     long begin =  2147483647, end = 0, level= -1;
1259     for (Iterator<JButton> i = selection.iterator(); i.hasNext()) {
1260       JButton button = i.next();
1261       Integer id = new Integer(button.getActionCommand());
1262 
1263       STreeNode child = nonTerminals.get(id);
1264 
1265       if (begin > child.getStart())
1266         begin = child.getStart();
1267       if (end < child.getEnd())
1268         end = child.getEnd();
1269       if (level < child.getLevel())
1270         level = child.getLevel();
1271 
1272       parentNode.add(child);
1273 
1274     //for
1275 
1276     parentNode.setLevel(level+1);
1277     parentNode.setStart(begin);
1278     parentNode.setEnd(end);
1279     parentNode.setUserObject(text);
1280     parentNode.createAnnotation(document,
1281                                 treeNodeAnnotationType,
1282                                 displayedString,
1283                                 utteranceStartOffset.longValue());
1284 
1285 
1286     return parentNode;
1287   }
1288 
1289   /**
1290     * Create a parent node for all selected non-terminal nodes
1291     */
1292   protected STreeNode createParentNode(String text, Annotation annot) {
1293     STreeNode  parentNode = new STreeNode(annot);
1294 
1295     long level = -1;
1296     for (Iterator<JButton> i = selection.iterator(); i.hasNext()) {
1297       JButton button = i.next();
1298       Integer id = new Integer(button.getActionCommand());
1299 
1300       STreeNode child = nonTerminals.get(id);
1301 
1302       if (level < child.getLevel())
1303         level = child.getLevel();
1304 
1305       parentNode.add(child);
1306     //for
1307 
1308     parentNode.setLevel(level+1);
1309     parentNode.setUserObject(text);
1310 
1311     return parentNode;
1312   }
1313 
1314 
1315   void selectNode(MouseEvent e) {
1316     // try finding the node that's annotated, i.e., the selected button
1317     if (e.getSource() instanceof JButton) {
1318       JButton source = (JButtone.getSource();
1319 
1320         selection.add(source);
1321         buttonBackground = source.getBackground();
1322         source.setBackground(selectedNodeColor);
1323     }
1324   }
1325 
1326   // remove that node from the syntax tree
1327   void removeNode(JButton button) {
1328 
1329     Integer id = new Integer(button.getActionCommand());
1330     STreeNode node = nonTerminals.get(id);
1331     nonTerminals.remove(id);
1332     node.removeAnnotation(document);
1333 
1334     //fix the STreeNodes involved
1335     resetChildren(node);
1336     removeNodesAbove(node);
1337 
1338     //remove button from everywhere
1339     buttons.remove(id);
1340     button.setVisible(false);
1341     this.remove(button);
1342 
1343     //recalculate all lines
1344     recalculateLines();
1345 
1346     //make sure we clear the selection
1347     selection.clear();
1348     repaint();
1349   }
1350 
1351   //set parent node to null for all children of the given node
1352   private void resetChildren(STreeNode node) {
1353     for (Enumeration<?> e = node.children(); e.hasMoreElements())
1354       ((STreeNodee.nextElement()).setParent(null);
1355 
1356     node.disconnectChildren();
1357   }
1358 
1359   private void removeNodesAbove(STreeNode node) {
1360     STreeNode parent = (STreeNodenode.getParent();
1361 
1362     while (parent != null) {
1363       Integer id = new Integer(parent.getID());
1364       parent.removeAnnotation(document);
1365       if (parent.isNodeChild(node))
1366         parent.remove(node);
1367       parent.disconnectChildren();
1368 
1369       nonTerminals.remove(id);
1370 
1371       JButton button = buttons.get(id);
1372       this.remove(button);
1373       buttons.remove(id);
1374 
1375       parent = (STreeNodeparent.getParent();
1376     }
1377   }
1378 
1379   private void recalculateLines() {
1380     lines.clear();
1381     //go through all non-terminals and recalculate their lines to their children
1382     for (Iterator<STreeNode> i = nonTerminals.values().iterator(); i.hasNext())
1383       recalculateLines(i.next());
1384 
1385   }
1386 
1387   /**
1388     * recalculates all lines from that node to all its children
1389     */
1390   private void recalculateLines(STreeNode node) {
1391     Integer id = new Integer(node.getID());
1392     JButton button = buttons.get(id);
1393 
1394     int bX = button.getX() + button.getWidth()/2;
1395     int bY = button.getY() + button.getHeight();
1396 
1397     for (Enumeration<?> e = node.children(); e.hasMoreElements()) {
1398       STreeNode subNode = (STreeNodee.nextElement();
1399       Integer sid = new Integer(subNode.getID());
1400       JButton subButton = buttons.get(sid);
1401 
1402       Coordinates coords = new Coordinates(
1403                                 bX,
1404                                 bY,
1405                                 subButton.getX() + subButton.getWidth()/2,
1406                                 subButton.getY());
1407 
1408       lines.add(coords);
1409     }
1410 
1411   }
1412 
1413 /*
1414   // discontinued from use,done automatically instead, when the utterance is set
1415 
1416   public void setTreeAnnotations(AnnotationSet newTreeAnnotations) {
1417     AnnotationSet  oldTreeAnnotations = treeAnnotations;
1418     treeAnnotations = newTreeAnnotations;
1419     firePropertyChange("treeAnnotations", oldTreeAnnotations,
1420                           newTreeAnnotations);
1421   }
1422 */
1423 
1424   public void setTreeNodeAnnotationType(String newTreeNodeAnnotationType) {
1425     treeNodeAnnotationType = newTreeNodeAnnotationType;
1426   }
1427 
1428   public String getTreeNodeAnnotationType() {
1429     return treeNodeAnnotationType;
1430   }
1431 
1432   public void setTokenType(String newTokenType) {
1433     if (newTokenType != null && ! newTokenType.equals(""))
1434       tokenType = newTokenType;
1435   }
1436 
1437   public String getTokenType() {
1438     return tokenType;
1439   }
1440 
1441   void this_componentShown(ComponentEvent e) {
1442     Out.println("Tree Viewer shown");
1443   }
1444 
1445   void this_componentHidden(ComponentEvent e) {
1446     Out.println("Tree Viewer closes");
1447   }
1448 
1449 /*
1450   //None of this works, damn!!!
1451 
1452   public void setVisible(boolean b) {
1453     if (!b && this.isVisible())
1454       Out.println("Tree Viewer closes");
1455 
1456     super.setVisible( b);
1457   }
1458   public void hide() {
1459     Out.println("Tree Viewer closes");
1460     super.hide();
1461   }
1462 */
1463 
1464   private static class FocusButton extends JButton {
1465   
1466     public FocusButton(String text) {
1467       super(text);
1468     }
1469   
1470     @SuppressWarnings("unused")
1471     public FocusButton() {
1472       super();
1473     }
1474   
1475     @SuppressWarnings("unused")
1476     public FocusButton(Icon icon) {
1477       super(icon);
1478     }
1479   
1480     @SuppressWarnings("unused")
1481     public FocusButton(String text, Icon icon) {
1482       super(text, icon);
1483     }// public FocusButton
1484     
1485   //  public boolean isManagingFocus() {
1486   //    return true;
1487   //  }// public boolean isManagingFocus()
1488   
1489     @Override
1490     public void processComponentKeyEvent(KeyEvent e) {
1491       super.processComponentKeyEvent(e);
1492   
1493       //I need that cause I get all events here, so I only want to process
1494       //when it's a release event. The reason is that for keys like <DEL>
1495       //key_typed never happens
1496       if (e.getID() != KeyEvent.KEY_RELEASED)
1497         return;
1498   
1499       if (e.getKeyCode() == KeyEvent.VK_DELETE) {
1500         SyntaxTreeViewer viewer = (SyntaxTreeViewer) ((JButtone.getSource()).getParent();
1501         viewer.removeNode((JButtone.getSource());
1502       }
1503     }// public void processComponentKeyEvent(KeyEvent e)
1504   
1505   }
1506 }// class SyntaxTreeViewer
1507 
1508 
1509 
1510 // $Log$
1511 // Revision 1.28  2005/10/10 10:47:08  valyt
1512 // Converted FocusButton from a phantom class to a static innner class (to make the dependency checker's life easier)
1513 //
1514 // Revision 1.27  2005/01/11 13:51:34  ian
1515 // Updating copyrights to 1998-2005 in preparation for v3.0
1516 //
1517 // Revision 1.26  2004/07/26 14:59:32  valyt
1518 // "Made in Sheffield" sources are now JDK 1.5 safe (by renaming enum to enumeration).
1519 // There are still problems with Java sources generated by JavaCC
1520 //
1521 // Revision 1.25  2004/07/21 17:10:07  akshay
1522 // Changed copyright from 1998-2001 to 1998-2004
1523 //
1524 // Revision 1.24  2004/03/25 13:01:05  valyt
1525 // Imports optimisation throughout the Java sources
1526 // (to get rid of annoying warnings in Eclipse)
1527 //
1528 // Revision 1.23  2003/08/27 15:53:03  valyt
1529 //
1530 // removed deprecation warning
1531 //
1532 // Revision 1.22  2003/01/28 10:01:16  marin
1533 // [marin] bugfixes from Kali
1534 //
1535 // Revision 1.21  2002/03/06 17:15:46  kalina
1536 // Reorganised the source code, so that it now uses constants from
1537 // ANNIEConstants, GateConstants and parameter constants defined on each PR.
1538 // Read e-mail to the gate list for an explanation.
1539 //
1540 // Revision 1.20  2001/08/08 16:14:26  kalina
1541 // A minor change to the tree viewer.
1542 //
1543 // Revision 1.19  2001/08/08 14:39:00  kalina
1544 // Made the dialog to size itself maximum as much as the screen, coz was
1545 // getting too big without that.
1546 //
1547 // Some documentation on Tree Viewer and some small changes to utterance2trees()
1548 // to make it order the tokens correctly by offset
1549 //
1550 // Revision 1.18  2001/08/07 19:03:05  kalina
1551 // Made the tree viewer use Token annotations to break the sentence for annotation
1552 //
1553 // Revision 1.17  2001/08/07 17:01:32  kalina
1554 // Changed the AVR implementing classes in line with the updated AVR
1555 // API (cancelAction() and setSpan new parameter).
1556 //
1557 // Also updated the TreeViewer, so now it can be used to edit and view
1558 // Sentence annotations and the SyntaxTreeNodes associated with them.
1559 // So if you have trees, it'll show them, if not, it'll help you build them.
1560 //
1561 // Revision 1.16  2001/04/09 10:36:36  oana
1562 // a few changes in the code style
1563 //
1564 // Revision 1.14  2000/12/04 12:29:29  valyt
1565 // Done some work on the visual resources
1566 // Added the smart XJTable
1567 //
1568 // Revision 1.13  2000/11/08 16:35:00  hamish
1569 // formatting
1570 //
1571 // Revision 1.12  2000/10/26 10:45:26  oana
1572 // Modified in the code style
1573 //
1574 // Revision 1.11  2000/10/24 10:10:18  valyt
1575 // Fixed the deprecation warning in gate/gui/SyntaxTreeViewer.java
1576 //
1577 // Revision 1.10  2000/10/18 13:26:47  hamish
1578 // Factory.createResource now working, with a utility method that uses reflection (via java.beans.Introspector) to set properties on a resource from the
1579 //     parameter list fed to createResource.
1580 //     resources may now have both an interface and a class; they are indexed by interface type; the class is used to instantiate them
1581 //     moved createResource from CR to Factory
1582 //     removed Transients; use Factory instead
1583 //
1584 // Revision 1.9  2000/10/16 16:44:32  oana
1585 // Changed the comment of DEBUG variable
1586 //
1587 // Revision 1.8  2000/10/10 15:36:35  oana
1588 // Changed System.out in Out and System.err in Err;
1589 // Added the DEBUG variable seted on false;
1590 // Added in the header the licence;
1591 //
1592 // Revision 1.7  2000/10/10 09:49:57  valyt
1593 // Fixed the Annotation test
1594 //
1595 // Revision 1.6  2000/10/02 12:34:06  valyt
1596 // Added the UnicodeEnabled switch on gate.util.Tools
1597 //
1598 // Revision 1.5  2000/09/28 14:26:09  kalina
1599 // Added even more documentation (is this me?!) and allowed several tokens to be
1600 // passed instead of a whole utterance/sentence for annotation. Needs good testing this
1601 // but will do it when somebody tries using this functionality.
1602 //
1603 // Revision 1.4  2000/09/28 13:16:12  kalina
1604 // Added some documentation
1605 //
1606 // Revision 1.3  2000/09/21 14:23:45  kalina
1607 // Fixed some small bug in main(). To test just run the component itself.
1608 //
1609 // Revision 1.2  2000/09/21 14:17:27  kalina
1610 // Added Unicode support
1611 //
1612 // Revision 1.1  2000/09/20 17:03:37  kalina
1613 // Added the tree viewer from the prototype. It works now with the new annotation API.