1   /*
2    *  SyntaxTreeViewer.java
3    *
4    *  Copyright (c) 1998-2001, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Kalina Bontcheva, 20/09/2000
12   *
13   *  $Id: SyntaxTreeViewer.java,v 1.21 2002/03/06 17:15:46 kalina Exp $
14   */
15  
16  package gate.gui;
17  
18  //java imports
19  import java.util.*;
20  import java.beans.*;
21  import java.net.URL;
22  
23  
24  //AWT imports - layouts and events
25  import java.awt.*;
26  import java.awt.event.*;
27  
28  //SWING imports
29  import javax.swing.*;
30  import javax.swing.event.PopupMenuListener;
31  import javax.swing.event.PopupMenuEvent;
32  
33  //GATE imports
34  import gate.util.*;
35  import gate.*;
36  import gate.creole.*;
37  
38  
39  /**
40    * The SyntaxTreeViewer is capable of showing and editing utterances (fancy
41    * name for sentences) and the
42    * attached syntax trees. It works by taking an utterance and all Token
43    * annotations and constructs the text. Then it also gets all SyntaxTreeNode
44    * annotations and builds and shows the syntac tree for that utterance. The
45    * leaves of the tree are the tokens, which constitute the utterance.<P>
46    *
47    * It is possible to configure the annotation types that are used by the
48    * viewer. The textAnnotationType property specifies the type
49    * of annotation which is used to denote the utterance (sentence).
50    * In GATE, the value of this property is not set directly, but is derived
51    * from the VR configuration information from creole.xml (explained below).
52    *
53    * The treeNodeAnnotationType is the name of the
54    * annotations which encode the SyntaxTreeNodes; default - SyntaxTreeNode.
55    * To change when part of GATE, modify the <PARAMETER> setting of the
56    * TreeViewer entry in creole.xml. Similarly, one can change which annotation
57    * is used for chunking the utterance. By default, it is Token, which is also
58    * specified in creole.xml as a parameter in the treeviewer entry.
59    *
60    * The component assumes that the annotations of type treeNodeAnnotationType have
61    * features called: cat with a value String; consists which is a List either
62    * empty or with annotation ids of the node's children; and optionally
63    * text which contains
64    * the text covered by this annotation. The component will work fine even
65    * without the last feature. Still when it creates annotations,
66    * these will have this feature added. <P>
67    *
68    *
69    * Newly added tree nodes to the tree are added to the document
70    * as annotations and deleted nodes are automatically deleted from the document
71    * only after OK is chosen in the dialog. Cancel does not make any changes
72    * permanent. <P>
73    *
74    * Configuring the viewer in GATE<P>
75    * The viewer is configured in creole.xml. The default entry is:
76    * <PRE>
77    *   <RESOURCE>
78    *     <NAME>Syntax tree viewer</NAME>
79    *     <CLASS>gate.gui.SyntaxTreeViewer</CLASS>
80    *     <!-- type values can be  "large" or "small"-->
81    *     <GUI>
82    *       <MAIN_VIEWER/>
83    *       <ANNOTATION_TYPE_DISPLAYED>Sentence</ANNOTATION_TYPE_DISPLAYED>
84    *       <PARAMETER NAME="treeNodeAnnotationType" DEFAULT="SyntaxTreeNode"
85    *                  RUNTIME="false" OPTIONAL="true">java.lang.String
86    *       </PARAMETER>
87    *       <PARAMETER NAME="tokenType" DEFAULT="Token" RUNTIME="false"
88    *                  OPTIONAL="true">java.lang.String
89    *       </PARAMETER>
90    *     </GUI>
91    *   </RESOURCE>
92    * </PRE>
93    *
94    * The categories that appear in the menu for manual annotation are determined
95    * from SyntaxTreeViewerSchema.xml. If you want to change the default set,
96    * you must edit this file and update your Gate jar accordingly (e.g., by
97    * recompilation. This does not affect the categories of SyntaxTreeNode
98    * annotations, which have been created automatically by some other process,
99    * e.g., a parser PR.
100   *
101   * <P>
102   * If used outside GATE,
103   * in order to have appropriate behaviour always put this component inside a
104   * scroll pane or something similar that provides scrollers.
105   * Example code: <BREAK>
106   * <PRE>
107   *  JScrollPane scroller = new JScrollPane(syntaxTreeViewer1);
108   *  scroller.setPreferredSize(syntaxTreeViewer1.getPreferredSize());
109   *  frame.getContentPane().add(scroller, BorderLayout.CENTER);
110   * </PRE>
111   *
112   *
113   * The default way is to pass just one annotation of type textAnnotationType
114   * which corresponds to the entire sentence or utterance to be annotated with
115   * syntax tree information. Then the viewer automatically tokenises it
116   * (by obtaining the relevant token annotations) and creates the leaves.<P>
117   *
118   * To create a new annotation, use setSpan, instead of setAnnotation.
119   *
120   * <P> In either
121   * case, you must call setTarget first, because that'll provide the viewer
122   * with the document's annotation set, from where it can obtain the token
123   * annotations.
124   * <P> If you intend to use the viewer outside GATE and do not understand
125   * the API, e-mail gate@dcs.shef.ac.uk.
126   */
127 
128 public class SyntaxTreeViewer extends AbstractVisualResource
129     implements  Scrollable, ActionListener, MouseListener,
130                 AnnotationVisualResource {
131 
132   /** The annotation type used to encode each tree node*/
133   public static final String TREE_NODE_ANNOTATION_TYPE = "SyntaxTreeNode";
134   /** The name of the feature that encodes the tree node's category information */
135   public static final String NODE_CAT_FEATURE_NAME = "cat";
136   /** The name of the feature that encodes the subtree annotations */
137   public static final String NODE_CONSISTS_FEATURE_NAME = "consists";
138 
139   // class members
140   // whether to use any layout or not
141   protected boolean laidOut = false;
142 
143   // display all buttons x pixels apart horizontally
144   protected int horizButtonGap = 5;
145 
146   // display buttons at diff layers x pixels apart vertically
147   protected int vertButtonGap = 50;
148 
149   // extra width in pixels to be added to each button
150   protected int extraButtonWidth = 10;
151 
152   // number of pixels to be used as increment by scroller
153   protected int maxUnitIncrement = 10;
154 
155   // GUI members
156   BorderLayout borderLayout1 = new BorderLayout();
157   JPopupMenu popup = new JPopupMenu(); //the right-click popup
158   Color buttonBackground;
159   Color selectedNodeColor = Color.red.darker();
160 
161   // the HashSet with the coordinates of the lines to draw
162   HashSet lines = new HashSet();
163 
164   // The utterance to be annotated as a sentence. It's not used if the tree
165   // is passed
166   // as annotations.
167   protected Annotation utterance;
168   protected Long utteranceStartOffset = new Long(0);
169   protected Long utteranceEndOffset = new Long(0);
170   protected AnnotationSet currentSet = null;
171 
172   protected String tokenType = ANNIEConstants.TOKEN_ANNOTATION_TYPE;
173 
174   // for internal use only. Set when the utterance is set.
175   protected String displayedString = "";
176 
177   // The name of the annotation type which is used to locate the
178   // stereotype with the allowed categories
179   // also when reading and creating annotations
180   protected String treeNodeAnnotationType = TREE_NODE_ANNOTATION_TYPE;
181 
182   // The annotation name of the annotations used to extract the
183   // text that appears at the leaves of the tree. For now the viewer
184   // supports only one such annotation but might be an idea to extend it
185   // so that it gets its text off many token annotations, which do not
186   // need to be tokenised or off the syntax tree annotations themselves.
187   protected String textAnnotationType = ANNIEConstants.SENTENCE_ANNOTATION_TYPE;
188 
189   // all leaf nodes
190   protected HashMap leaves = new HashMap();
191 
192   // all non-terminal nodes
193   protected HashMap nonTerminals = new HashMap();
194 
195   // all buttons corresponding to any node
196   protected HashMap buttons = new HashMap();
197 
198   // all selected buttons
199   protected Vector selection = new Vector();
200 
201   // all annotations to be displayed
202   protected AnnotationSet treeAnnotations;
203 
204   protected Document document = null;
205   // the document to which the annotations belong
206 
207   //true when a new utterance annotation has been added
208   //then if the user presses cancel, I need to delete it
209   protected boolean utteranceAdded = false;
210 
211 
212   public SyntaxTreeViewer() {
213     try  {
214       jbInit();
215     }
216     catch(Exception ex) {
217       ex.printStackTrace(Err.getPrintWriter());
218     }
219 
220   }
221 
222   //CONSTRUCTORS
223   private SyntaxTreeViewer(String annotType) {
224 
225     treeNodeAnnotationType = annotType;
226     try  {
227       jbInit();
228     }
229     catch(Exception ex) {
230       ex.printStackTrace(Err.getPrintWriter());
231     }
232   }
233 
234   //METHODS
235   private void jbInit() throws Exception {
236 
237     //check if we're using a layout; preferrably not
238     if (laidOut)
239       this.setLayout(borderLayout1);
240     else
241       this.setLayout(null);
242 
243     this.setPreferredSize(new Dimension (600, 400));
244     this.setSize(600, 400);
245     this.setBounds(0, 0, 600, 400);
246     this.addComponentListener(new java.awt.event.ComponentAdapter() {
247       public void componentShown(ComponentEvent e) {
248         this_componentShown(e);
249       }
250       public void componentHidden(ComponentEvent e) {
251         this_componentHidden(e);
252       }
253     });
254     this.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
255 
256       public void propertyChange(PropertyChangeEvent e) {
257         this_propertyChange(e);
258       }
259     });
260 
261     buttonBackground = Color.red; //this.getBackground();
262 
263     //get all categories from stereotype
264     fillCategoriesMenu();
265 
266     //initialise the popup menu
267 
268     //add popup to container
269     this.add(popup);
270   }// private void jbInit()
271 
272   // Methods required by AnnotationVisualResource
273 
274   /**
275     * Called by the GUI when this viewer/editor has to initialise itself for a
276     * specific annotation or text span.
277     * @param target the object which will always be a {@link gate.AnnotationSet}
278     */
279 
280   public void setTarget(Object target) {
281     if (target == null) return;
282     currentSet = (AnnotationSet) target;
283     document = currentSet.getDocument();
284   }
285 
286   /**
287     * Used when the viewer/editor has to display/edit an existing annotation
288     * @param ann the annotation to be displayed or edited. If ann is null then
289     * the method simply returns
290     */
291   public void setAnnotation(Annotation ann){
292     if (ann == null) return;
293 
294     utterance = ann;
295     utteranceStartOffset = utterance.getStartNode().getOffset();
296     utteranceEndOffset = utterance.getEndNode().getOffset();
297     textAnnotationType = ann.getType();
298 
299     clearAll();
300     utterances2Trees();
301     annotations2Trees();
302     this.setVisible(true);
303     repaint();
304   }
305 
306   /**
307     * Used when the viewer has to create new annotations.
308     * @param startOffset the start offset of the span covered by the new
309     * annotation(s). If is <b>null</b> the method will simply return.
310     * @param endOffset the end offset of the span covered by the new
311     * annotation(s). If is <b>null</b> the method will simply return.
312     */
313   public void setSpan(Long startOffset, Long endOffset, String annotType){
314     // If one of them is null, then simply return.
315     if (startOffset == null || endOffset == null) return;
316     if (document == null) return;
317 
318     try {
319       Integer newId = currentSet.add( startOffset, endOffset, annotType,
320                                 Factory.newFeatureMap());
321       utterance = currentSet.get(newId);
322       utteranceAdded = true;
323       textAnnotationType = annotType;
324       setAnnotation(utterance);
325 
326     } catch (InvalidOffsetException ioe) {
327       ioe.printStackTrace(Err.getPrintWriter());
328     }
329 
330   }
331 
332   /**
333    * Called by the GUI when the user has pressed the "OK" button. This should
334    * trigger the saving of the newly created annotation(s)
335    */
336   public void okAction() throws GateException{
337     //Out.println("Visible coords" + this.getVisibleRect().toString());
338     //Out.println("Size" + this.getBounds().toString());
339     STreeNode.transferAnnotations(document, currentSet);
340 
341   } //okAction()
342 
343   /**
344    * Called by the GUI when the user has pressed the "Cancel" button. This should
345    * trigger the cleanup operation
346    */
347   public void cancelAction() throws GateException{
348     //if we added a new utterance but user does not want it any more...
349     if (utteranceAdded) {
350       currentSet.remove(utterance); //delete it
351       utteranceAdded = false;
352     }
353     //also cleanup the temporary annotation sets used by the viewer
354     //to cache the added and deleted tree annotations
355     STreeNode.undo(document);
356 
357   } //okAction()
358 
359 
360   /**
361     * Checks whether this viewer/editor can handle a specific annotation type.
362     * @param annotationType represents the annotation type being questioned.If
363     * it is <b>null</b> then the method will return false.
364     * @return true if the SchemaAnnotationEditor can handle the annotationType
365     * or false otherwise.
366     */
367   public boolean canDisplayAnnotationType(String annotationType){
368     // Returns true only if the there is an AnnotationSchema with the same type
369     // as annotationType.
370     if (annotationType == null) return false;
371     boolean found = false;
372 
373     java.util.List specificEditors = Gate.getCreoleRegister().
374                                      getAnnotationVRs(annotationType);
375     Iterator editorIter = specificEditors.iterator();
376     while(editorIter.hasNext() && !found){
377       String editorClass = (String)editorIter.next();
378 
379       Out.println(editorClass);
380       if (editorClass.indexOf(this.getClass().getName()) > -1) {
381         textAnnotationType = annotationType;
382         found = true;
383       }
384     }
385 
386     return found;
387   }// canDisplayAnnotationType();
388 
389 
390 /*  public static void main(String[] args) throws Exception {
391     Gate.init();
392     // final String text = "This is a sentence. That is another one.";
393     final String text = "???????? ?????? Kalina";
394     final Document doc = Factory.newDocument(text);
395 
396     // that works too but only use if you have the test file there.
397     // final Document doc = Factory.newDocument(
398     //                        new URL("file:///z:/temp/weird.txt"), "UTF-8");
399 
400 
401     final SyntaxTreeViewer syntaxTreeViewer1 =
402       new SyntaxTreeViewer("SyntaxTreeNode");
403     //syntaxTreeViewer1.setUnicodeSupportEnabled(true);
404     //need to set the document here!!!!
405 
406 
407     JFrame frame = new JFrame();
408 
409     //INITIALISE THE FRAME, ETC.
410     frame.setEnabled(true);
411     frame.setTitle("SyntaxTree Viewer");
412     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
413 
414     // frame.getContentPane().add(syntaxTreeViewer1, BorderLayout.CENTER);
415     // intercept the closing event to shut the application
416     frame.addWindowListener(new WindowAdapter() {
417       public void windowClosing(WindowEvent e) {
418         AnnotationSet hs = doc.getAnnotations().get("SyntaxTreeNode");
419         if (hs != null && hs.size() > 0) {
420           int k = 0;
421           for (Iterator i = hs.iterator(); i.hasNext(); k++) {
422             Out.println("Tree Annot " + k + ": ");
423             Out.println(i.next().toString());
424           }
425         } //if
426         Out.println("Exiting...");
427         //System.exit(0);
428       }
429     });
430 
431     //Put the bean in a scroll pane.
432     JScrollPane scroller = new JScrollPane(syntaxTreeViewer1);
433     scroller.setPreferredSize(syntaxTreeViewer1.getPreferredSize());
434     frame.getContentPane().add(scroller, BorderLayout.CENTER);
435 
436     //DISPLAY FRAME
437     frame.pack();
438     frame.show();
439 
440     FeatureMap attrs = Factory.newFeatureMap();
441     attrs.put("time", new Long(0));
442     attrs.put("text", doc.getContent().toString());
443 */
444     /*
445     FeatureMap attrs1 = Factory.newFeatureMap();
446     attrs1.put("cat", "N");
447     attrs1.put("text", "This");
448     attrs1.put("consists", new Vector());
449 
450     FeatureMap attrs2 = Factory.newFeatureMap();
451     attrs2.put("cat", "V");
452     attrs2.put("text", "is");
453     attrs2.put("consists", new Vector());
454     */
455 
456 /*
457     doc.getAnnotations().add( new Long(0), new Long(
458                       doc.getContent().toString().length()),"utterance", attrs);
459 */
460     /* Integer id1 = doc.getAnnotations().add(new Long(0), new Long(4),
461                               "SyntaxTreeNode", attrs1);
462     Integer id2 = doc.getAnnotations().add(new Long(5), new Long(7),
463                               "SyntaxTreeNode", attrs2);
464 
465     FeatureMap attrs3 = Factory.newFeatureMap();
466     attrs3.put("cat", "VP");
467     attrs3.put("text", "This is");
468     Vector consists = new Vector();
469     consists.add(id1);
470     consists.add(id2);
471     attrs3.put("consists", consists);
472     doc.getAnnotations().add(new Long(0), new Long(7),
473                                                   "SyntaxTreeNode", attrs3);
474     */
475 
476 /*
477     HashSet set = new HashSet();
478     set.add("utterance");
479     set.add("SyntaxTreeNode");
480     AnnotationSet annots = doc.getAnnotations().get(set);
481     syntaxTreeViewer1.setTreeAnnotations(annots);
482 
483   }// public static void main
484 */
485 
486   protected void paintComponent(Graphics g) {
487     super.paintComponent( g);
488     drawLines(g);
489   }// protected void paintComponent(Graphics g)
490 
491 
492   private void drawLines(Graphics g) {
493 
494     for (Iterator i = lines.iterator(); i.hasNext(); ) {
495       Coordinates coords = (Coordinates) i.next();
496 
497       g.drawLine( coords.getX1(),
498                   coords.getY1(),
499                   coords.getX2(),
500                   coords.getY2());
501     }// for
502   }// private void drawLines(Graphics g)
503 
504   public Dimension getPreferredScrollableViewportSize() {
505         return getPreferredSize();
506   }// public Dimension getPreferredScrollableViewportSize()
507 
508   public int getScrollableUnitIncrement(Rectangle visibleRect,
509                                               int orientation, int direction) {
510     return maxUnitIncrement;
511   }// public int getScrollableUnitIncrement
512 
513   public int getScrollableBlockIncrement(Rectangle visibleRect,
514                                               int orientation, int direction) {
515     if (orientation == SwingConstants.HORIZONTAL)
516         return visibleRect.width - maxUnitIncrement;
517     else
518         return visibleRect.height - maxUnitIncrement;
519   }// public int getScrollableBlockIncrement
520 
521   public boolean getScrollableTracksViewportWidth() {
522     return false;
523   }// public boolean getScrollableTracksViewportWidth()
524 
525   public boolean getScrollableTracksViewportHeight() {
526     return false;
527   }
528 
529   void this_propertyChange(PropertyChangeEvent e) {
530 
531     //we have a new utterance to display and annotate
532     if (e.getPropertyName().equals("utterance")) {
533       clearAll();
534       utterances2Trees();
535     }
536 
537   } //this_propertyChange
538 
539   /**
540     * Clear all buttons and tree nodes created because component is being
541     * re-initialised. Not sure it works perfectly.
542     */
543   private void clearAll() {
544     lines.clear();
545     this.removeAll();
546     buttons.clear();
547     leaves.clear();
548     nonTerminals.clear();
549   }
550 
551   /**
552     * Converts the annotations into treeNodes
553     */
554   private void annotations2Trees() {
555     if (document == null) return;
556 
557     HashMap processed = new HashMap(); //for all processed annotations
558 
559     //first get all tree nodes in this set, then restrict them by offset
560     AnnotationSet tempSet = currentSet.get(treeNodeAnnotationType);
561     if (tempSet == null || tempSet.isEmpty())
562       return;
563     treeAnnotations = tempSet.get(utterance.getStartNode().getOffset(),
564                                   utterance.getEndNode().getOffset());
565     if (treeAnnotations == null || treeAnnotations.isEmpty())
566       return;
567 
568     // sort them from left to right first
569     // Should work as
570     // annotation implements Comparable
571     LinkedList nodeAnnots = new LinkedList(treeAnnotations);
572     Collections.sort(nodeAnnots);
573 
574     Vector childrenButtons = new Vector();
575     String oldParent = "";
576 
577     //find all annotations with no children
578     Iterator i = nodeAnnots.iterator();
579     while (i.hasNext()) {
580       Annotation annot = (Annotation) i.next();
581 
582       java.util.List children =
583         (java.util.List) annot.getFeatures().get(NODE_CONSISTS_FEATURE_NAME);
584       //check if it's a leaf
585       if (children == null ||
586           children.isEmpty())
587         {
588 
589         STreeNode leaf = findLeaf(annot.getStartNode(), annot.getEndNode());
590         if (leaf == null) {//not found
591           Out.println("Can't find my leaf node for annotation: " + annot);
592         }
593 
594         JButton button = (JButton) buttons.get(new Integer(leaf.getID()));
595         selection.clear();
596         selection.add(button);
597 
598         //then create the non-terminal with the category
599         STreeNode node = new STreeNode(annot);
600         node.add(leaf);
601         node.setLevel(1);
602         node.setUserObject(annot.getFeatures().get(NODE_CAT_FEATURE_NAME));
603         nonTerminals.put(new Integer(node.getID()), node);
604         JButton parentButton = createCentralButton(node);
605         addLines(node);
606 
607         //finally add to the processed annotations
608         processed.put(annot.getId(), parentButton);
609 
610       } //if
611 
612     } //loop through children
613 
614     //loop through the rest of the nodes
615     Iterator i1 = nodeAnnots.iterator();
616     while (i1.hasNext()) {
617       Annotation annotNode = (Annotation) i1.next();
618       if (processed.containsKey(annotNode.getId()))
619         continue;
620       processChildrenAnnots(annotNode, processed);
621     } //process all higher nodes
622 
623     selection.clear();
624 
625     this.scrollRectToVisible(new
626       Rectangle(0, (int) getHeight()- (int) getVisibleRect().getHeight(),
627         (int) getVisibleRect().getWidth(), (int) getVisibleRect().getHeight()));
628   } //annotations2Trees
629 
630   private JButton processChildrenAnnots(Annotation annot, HashMap processed) {
631     selection.clear();
632     Vector childrenButtons = new Vector();
633     java.util.List children =
634       (java.util.List) annot.getFeatures().get(NODE_CONSISTS_FEATURE_NAME);
635 
636     for (Iterator i = children.iterator(); i.hasNext(); ) {
637       Integer childId = (Integer) i.next();
638       Annotation child = treeAnnotations.get(childId);
639       JButton childButton;
640 
641       if (processed.containsKey(child.getId()))
642         childButton = (JButton) processed.get(child.getId());
643       else
644         childButton = processChildrenAnnots(child, processed);
645 
646       childrenButtons.add(childButton);
647     }
648 
649     selection = (Vector) childrenButtons.clone();
650     STreeNode parent = createParentNode(
651                           (String) annot.getFeatures().get(NODE_CAT_FEATURE_NAME),
652                           annot);
653     nonTerminals.put(new Integer(parent.getID()), parent);
654     JButton parentButton = createCentralButton(parent);
655     addLines(parent);
656 
657     processed.put(annot.getId(), parentButton);
658     selection.clear();
659     return parentButton;
660   }// private JButton processChildrenAnnots
661 
662   private STreeNode findLeaf(Node start, Node end) {
663     for (Iterator i = leaves.values().iterator(); i.hasNext(); ) {
664       STreeNode node = (STreeNode) i.next();
665       if (node.getStart() == start.getOffset().intValue() &&
666           node.getEnd() == end.getOffset().intValue()
667          )
668         return node;
669     }
670 
671     return null;
672   }//private STreeNode findLeaf(Node start, Node end)
673 
674 
675   /**
676     * Converts the given utterances into a set of leaf nodes for annotation
677     */
678   private void utterances2Trees() {
679 
680     if (! utterance.getType().equals(textAnnotationType)) {
681       Out.println("Can't display annotations other than the specified type:" +
682                                                             textAnnotationType);
683       return;
684     }
685 
686     // set the utterance offset correctly.
687     // All substring calculations depend on that.
688     utteranceStartOffset = utterance.getStartNode().getOffset();
689     utteranceEndOffset = utterance.getEndNode().getOffset();
690 
691     try {
692       displayedString = currentSet.getDocument().getContent().getContent(
693                         utteranceStartOffset, utteranceEndOffset).toString();
694     } catch (InvalidOffsetException ioe) {
695       ioe.printStackTrace(Err.getPrintWriter());
696     }
697 
698     AnnotationSet allTokens = currentSet.get(utteranceStartOffset,
699                                           utteranceEndOffset);
700     if (allTokens == null || allTokens.isEmpty()) {
701       Out.println("TreeViewer warning: No annotations of type " + tokenType +
702                   "so cannot show or edit the text and the tree annotations.");
703       return;
704     }
705 
706     AnnotationSet tokens = allTokens.get(tokenType);
707 
708     //if no tokens return
709     //needs improving maybe, just show one solid utterance
710     if (tokens == null || tokens.isEmpty()){
711       Out.println("TreeViewer warning: No annotations of type " + tokenType +
712                   "so cannot show or edit the text and the tree annotations.");
713       return;
714     }
715 
716     Insets insets = this.getInsets();
717     // the starting X position for the buttons
718     int buttonX = insets.left;
719 
720     // the starting Y position
721     int buttonY = this.getHeight() - 20 - insets.bottom;
722 
723     //We need to go through the nodes this way and get the f***ing tokens
724     //coz there is no way to sort them by startOffset. The compareTo method
725     //only uses the Ids, highly useful!
726     Node startNode = tokens.firstNode();
727     Node endNode = tokens.nextNode(startNode);
728 
729     //loop through the tokens
730     while (startNode != null && endNode != null) {
731       AnnotationSet nextTokenSet = tokens.get(startNode.getOffset());
732 
733       if (nextTokenSet == null || nextTokenSet.isEmpty())
734         break;
735 
736       Annotation tokenAnnot = (Annotation) nextTokenSet.iterator().next();
737       Long tokenBegin = tokenAnnot.getStartNode().getOffset();
738       Long tokenEnd = tokenAnnot.getEndNode().getOffset();
739 
740       String tokenText = "";
741       try {
742         tokenText = document.getContent().getContent(
743                         tokenBegin, tokenEnd).toString();
744       } catch (InvalidOffsetException ioe) {
745         ioe.printStackTrace(Err.getPrintWriter());
746       }
747 
748       // create the leaf node
749       STreeNode node =
750         new STreeNode(tokenBegin.longValue(), tokenEnd.longValue());
751 
752       // make it a leaf
753       node.setAllowsChildren(false);
754 
755       // set the text
756       node.setUserObject(tokenText);
757       node.setLevel(0);
758 
759       // add to hash table of leaves
760       leaves.put(new Integer(node.getID()), node);
761 
762       // create the corresponding button
763       buttonX = createButton4Node(node, buttonX, buttonY);
764 
765       //advance to the next node
766       startNode = tokenAnnot.getEndNode();
767       endNode = tokens.nextNode(startNode);
768     } //while
769 
770 
771 /*
772     //This old piece of code was used to tokenise, instead of relying on
773     // annotations. Can re-instate if someone shows me the need for it.
774 
775     long currentOffset = utteranceStartOffset.longValue();
776 
777     StrTokeniser strTok =
778         new StrTokeniser(displayedString,
779                         " \r\n\t");
780 
781     Insets insets = this.getInsets();
782     // the starting X position for the buttons
783     int buttonX = insets.left;
784 
785     // the starting Y position
786     int buttonY = this.getHeight() - 20 - insets.bottom;
787 
788     while (strTok.hasMoreTokens()) {
789       String word = strTok.nextToken();
790 //      Out.println("To display:" + word);
791 
792       // create the leaf node
793       STreeNode node =
794         new STreeNode(currentOffset, currentOffset + word.length());
795 
796       // make it a leaf
797       node.setAllowsChildren(false);
798 
799       // set the text
800       node.setUserObject(word);
801       node.setLevel(0);
802 
803       // add to hash table of leaves
804       leaves.put(new Integer(node.getID()), node);
805 
806       // create the corresponding button
807       buttonX = createButton4Node(node, buttonX, buttonY);
808 
809       currentOffset += word.length()+1;  //// +1 to include the delimiter too
810     }
811 */
812 
813     this.setSize(buttonX, buttonY + 20 + insets.bottom);
814     // this.resize(buttonX, buttonY + 20 + insets.bottom);
815     this.setPreferredSize(this.getSize());
816 
817   } // utterance2Trees
818 
819   /**
820     * Returns the X position where another button can start if necessary.
821     * To be used to layout only the leaf buttons. All others must be created
822     * central to their children using createCentralButton.
823     */
824   private int createButton4Node(STreeNode node, int buttonX, int buttonY) {
825 
826     JButton button = new JButton((String) node.getUserObject());
827     button.setBorderPainted(false);
828 
829     FontMetrics fm = button.getFontMetrics(button.getFont());
830 
831     int buttonWidth,
832         buttonHeight;
833 
834     // Out.print
835     //  ("Button width " + b1.getWidth() + "Button height " + b1.getHeight());
836 
837     buttonWidth = fm.stringWidth(button.getText())
838                   + button.getMargin().left + button.getMargin().right
839                   + extraButtonWidth;
840     buttonHeight = fm.getHeight() + button.getMargin().top +
841                       button.getMargin().bottom;
842     buttonY = buttonY - buttonHeight;
843 
844 //     Out.print("New Button X " + buttonX +
845 //        "New Button Y" + buttonY);
846 
847     button.setBounds(buttonX, buttonY, buttonWidth, buttonHeight);
848     button.addActionListener(this);
849     button.addMouseListener(this);
850     button.setActionCommand("" + node.getID());
851     button.setVisible(true);
852     button.enable();
853 
854     this.add(button);
855     buttons.put(new Integer(node.getID()), button);
856 
857     buttonX += buttonWidth + horizButtonGap;
858     return buttonX;
859 
860   }// private int createButton4Node(STreeNode node, int buttonX, int buttonY)
861 
862   private JButton createCentralButton(STreeNode newNode) {
863 
864     FocusButton button = new FocusButton((String) newNode.getUserObject());
865     button.setBorderPainted(false);
866 
867     FontMetrics fm = button.getFontMetrics(button.getFont());
868 
869     int buttonWidth,
870         buttonHeight,
871         buttonX = 0,
872         buttonY =0;
873 
874     // Out.print("Button width " + b1.getWidth() + ";
875     //    Button height " + b1.getHeight());
876 
877     buttonWidth = fm.stringWidth(button.getText())
878                   + button.getMargin().left + button.getMargin().right
879                   + extraButtonWidth;
880     buttonHeight = fm.getHeight() + button.getMargin().top +
881                       button.getMargin().bottom;
882 
883     int left = this.getWidth(), right =0 , top = this.getHeight();
884 
885     // determine the left, right, top
886     for (Iterator i = selection.iterator(); i.hasNext(); ) {
887       JButton childButton = (JButton) i.next();
888 
889       if (left > childButton.getX())
890         left = childButton.getX();
891       if (childButton.getX() + childButton.getWidth() > right)
892         right = childButton.getX() + childButton.getWidth();
893       if (childButton.getY() < top)
894         top = childButton.getY();
895     }
896 
897     buttonX = (left + right) /2 - buttonWidth/2;
898     buttonY = top - vertButtonGap;
899     // Out.println("Button's Y is" + buttonY);
900 
901     // Out.print("New Button width " + buttonWidth + ";
902     //    New Button height " + buttonHeight);
903     button.setBounds(buttonX, buttonY, buttonWidth, buttonHeight);
904     button.addActionListener(this);
905     button.addMouseListener(this);
906     // button.registerKeyboardAction(this,
907     //                          "delete",
908     //                           KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
909     //                           WHEN_FOCUSED);
910 
911     button.setActionCommand("" + newNode.getID());
912 
913     this.add(button);
914     // add to hashmap of buttons
915     buttons.put(new Integer(newNode.getID()), button);
916 
917     // check if we need to resize the panel
918     if (buttonY < 0) {
919       this.setSize(this.getWidth(), this.getHeight() + 5* (- buttonY));
920       this.setPreferredSize(this.getSize());
921       shiftButtonsDown(5* (-buttonY));
922     }
923 
924     return button;
925   }// private JButton createCentralButton(STreeNode newNode)
926 
927   private void shiftButtonsDown(int offset) {
928     for (Iterator i = buttons.values().iterator(); i.hasNext(); ) {
929       JButton button = (JButton) i.next();
930       button.setBounds(   button.getX(),
931                           button.getY() + offset,
932                           button.getWidth(),
933                           button.getHeight());
934     } // for loop through buttons
935 
936     for (Iterator k = lines.iterator(); k.hasNext(); ) {
937       Coordinates coords = (Coordinates) k.next();
938       coords.setY1(coords.getY1() + offset);
939       coords.setY2(coords.getY2() + offset);
940     }
941   }// private void shiftButtonsDown(int offset)
942 
943   public void actionPerformed(ActionEvent e) {
944 
945     //check for the popup menu items
946     if (e.getSource() instanceof JMenuItem) {
947       JMenuItem menuItem = (JMenuItem) e.getSource();
948 
949       // check if we're annotating a leaf
950       // the popup label is set to leaves when the popup has been
951       // constructed in showRightClickPopup
952       if (popup.getLabel().equals("leaves")) {
953         Integer id = new Integer(e.getActionCommand());
954 
955         clearSelection();
956         JButton button = (JButton) buttons.get(id);
957         selection.add(button);
958 
959         STreeNode leaf = (STreeNode) leaves.get(id);
960 
961         // create parent with the same span as leaf
962         // using createParentNode here is not a good idea coz it works only
963         // for creating parents of non-terminal nodes, not leaves
964         STreeNode parent = new STreeNode(leaf.getStart(), leaf.getEnd());
965         parent.setLevel(leaf.getLevel()+1); //levels increase from bottom to top
966         parent.add(leaf);
967 
968         // set the text
969         parent.setUserObject(menuItem.getText());
970 
971         // last create the annotation; should always come last!
972         parent.createAnnotation(  document,
973                                   treeNodeAnnotationType,
974                                   displayedString,
975                                   utteranceStartOffset.longValue());
976         nonTerminals.put(new Integer(parent.getID()), parent);
977 
978         // create new button positioned centrally above the leaf
979         createCentralButton(parent);
980 
981         // add the necessary lines for drawing
982         addLines(parent);
983 
984         selection.clear();
985 
986         // repaint the picture!
987         this.repaint();
988       } // finished processing leaves
989       else if (popup.getLabel().equals("non-terminal")) {
990         // the action command is set to the id under which
991         // the button can be found
992         Integer id = new Integer(e.getActionCommand());
993 
994         //locate button from buttons hashMap and add to selection
995         JButton button = (JButton) buttons.get(id);
996         selection.add(button);
997 
998         //create the new parent
999         STreeNode parent = createParentNode(menuItem.getText());
1000
1001        //add to nonTerminals HashMap
1002        nonTerminals.put(new Integer(parent.getID()), parent);
1003
1004        //create new button positioned centrally above the leaf
1005        createCentralButton(parent);
1006
1007        //add the necessary lines for drawing
1008        addLines(parent);
1009
1010        clearSelection();
1011
1012        //repaint the picture!
1013        this.repaint();
1014
1015      } //check for non-terminals
1016
1017    } //if statement for MenuItems
1018
1019
1020  }// public void actionPerformed(ActionEvent e)
1021
1022  public void mouseClicked(MouseEvent e) {
1023
1024    if (! (e.getSource() instanceof JButton))
1025      return;
1026
1027    JButton source = (JButton) e.getSource();
1028
1029    //check if CTRL or Shift is pressed and if not, clear the selection
1030    if ((! (e.isControlDown() || e.isShiftDown()))
1031         && SwingUtilities.isLeftMouseButton(e))
1032      clearSelection();
1033
1034    //and select the current node
1035    if (SwingUtilities.isLeftMouseButton(e))
1036    //if (e.getModifiers() == e.BUTTON1_MASK)
1037      selectNode(e);
1038
1039
1040    //only repspond to right-clicks here by displaying the popup
1041    if (SwingUtilities.isRightMouseButton(e)) {
1042      //if button not in focus, grad the focus and select it!
1043      if ( source.getBackground() != selectedNodeColor ) {
1044        source.grabFocus();
1045        source.doClick();
1046        selectNode(e);
1047      }
1048      //Out.println(e.getComponent().getClass() + " right-clicked!");
1049      showRightClickPopup(e);
1050    } //end of right-click processing
1051
1052  }// public void mouseClicked(MouseEvent e)
1053
1054  public void mousePressed(MouseEvent e) {
1055  }
1056
1057  public void mouseReleased(MouseEvent e) {
1058  }
1059
1060  public void mouseEntered(MouseEvent e) {
1061  }
1062
1063  public void mouseExited(MouseEvent e) {
1064  } // createButton4Node
1065
1066
1067  private void showRightClickPopup(MouseEvent e) {
1068
1069    //that'll always work coz we checked it in MouseClicked.
1070    JButton source = (JButton) e.getSource();
1071    Integer id = new Integer(source.getActionCommand());
1072
1073    //check if it's a leaf and if so, offer the leaf annotation dialog
1074    Object obj = leaves.get(id);
1075    if (obj != null) {
1076      STreeNode leaf = (STreeNode) obj;
1077      //do nothing if it already has a parent
1078      if (leaf.getParent() != null) {
1079        clearSelection();
1080        JOptionPane.showMessageDialog(
1081          this,
1082          "Node already annotated. To delete the existing annotation, " +
1083          "select it and press <DEL>.",
1084          "Syntax Tree Viewer message",
1085          JOptionPane.INFORMATION_MESSAGE);
1086        return;
1087      }
1088
1089      //reset the popup and set it's heading accordingly
1090      popup.setLabel("leaves");
1091      setMenuCommands(popup, ""+id);
1092
1093      popup.pack();
1094      popup.show(source, e.getX(), e.getY());
1095    } else { //we have a non-terminal node
1096
1097      //check if it has been annotated already
1098      if ( ((STreeNode) nonTerminals.get(id)).getParent() != null) {
1099        clearSelection();
1100        JOptionPane.showMessageDialog(this, "Node already annotated. To delete"+
1101                          " the existing annotation, select it and press <DEL>.",
1102                          "Syntax Tree Viewer message",
1103                          JOptionPane.INFORMATION_MESSAGE);
1104        return;  //and do nothing if so!
1105      }
1106
1107      popup.setLabel("non-terminal");
1108      setMenuCommands(popup, ""+id);
1109
1110      popup.pack();
1111      popup.show(source, e.getX(), e.getY());
1112
1113    }
1114
1115  } //showRightClickPopup
1116
1117  private void addLines(STreeNode newNode) {
1118
1119    JButton newButton = (JButton) buttons.get(new Integer(newNode.getID()));
1120    int nbX = newButton.getX() + newButton.getWidth()/2;
1121    int nbY = newButton.getY() + newButton.getHeight();
1122
1123    for (Iterator i = selection.iterator(); i.hasNext(); ) {
1124      JButton selButton = (JButton) i.next();
1125
1126      //I create it a rect but it will in fact be used as x1, y1, x2, y2 for the
1127      //draw line. see drawLines.
1128      Coordinates coords = new Coordinates(
1129                                nbX,
1130                                nbY,
1131                                selButton.getX() + selButton.getWidth()/2,
1132                                selButton.getY());
1133
1134      lines.add(coords);
1135    }
1136
1137  } // addLines
1138
1139  private void clearSelection() {
1140    for (Enumeration enum = selection.elements(); enum.hasMoreElements(); ) {
1141      JButton selButton = (JButton) enum.nextElement();
1142      selButton.setBackground(buttonBackground);
1143    }
1144
1145    selection.clear();
1146
1147  } //clearSlection
1148
1149
1150  private void fillCategoriesMenu() {
1151    boolean found = false;
1152
1153    //fetch the valid categories from the stereotype
1154    CreoleRegister creoleReg = Gate.getCreoleRegister();
1155    java.util.List currentAnnotationSchemaList =
1156                      creoleReg.getLrInstances("gate.creole.AnnotationSchema");
1157    if (currentAnnotationSchemaList.isEmpty()) return;
1158
1159    Iterator iter = currentAnnotationSchemaList.iterator();
1160    while (iter.hasNext()){
1161      AnnotationSchema annotSchema = (AnnotationSchema) iter.next();
1162      //we have found the right schema
1163      if (treeNodeAnnotationType.equals(annotSchema.getAnnotationName())) {
1164        found = true;
1165        FeatureSchema categories = annotSchema.getFeatureSchema(NODE_CAT_FEATURE_NAME);
1166        //iterate through all categories
1167        for (Iterator i =
1168                categories.getPermissibleValues().iterator(); i.hasNext(); ) {
1169
1170          JMenuItem menuItem = new JMenuItem( (String) i.next() );
1171          menuItem.addActionListener(this);
1172          popup.add(menuItem);
1173        } //for
1174
1175      } //if
1176    }// while
1177
1178    //if we don't have a schema, issue a warning
1179    if (! found)
1180      Out.println("Warning: You need to define an annotation schema for " +
1181                  treeNodeAnnotationType +
1182                  " in order to be able to add such annotations.");
1183
1184  } // fillCategoriesMenu
1185
1186  /**
1187    * Sets the action commands of all menu items to the specified command
1188    */
1189  private void setMenuCommands(JPopupMenu menu, String command) {
1190    for (int i = 0; i < menu.getComponentCount() ; i++) {
1191      JMenuItem item = (JMenuItem) menu.getComponent(i);
1192      item.setActionCommand(command);
1193    }
1194
1195  } // setMenuCommands
1196
1197  /**
1198    * Create a parent node for all selected non-terminal nodes
1199    */
1200  protected STreeNode createParentNode(String text) {
1201    STreeNode  parentNode = new STreeNode();
1202
1203    long begin =  2147483647, end = 0, level= -1;
1204    for (Iterator i = selection.iterator(); i.hasNext(); ) {
1205      JButton button = (JButton) i.next();
1206      Integer id = new Integer(button.getActionCommand());
1207
1208      STreeNode child = (STreeNode) nonTerminals.get(id);
1209
1210      if (begin > child.getStart())
1211        begin = child.getStart();
1212      if (end < child.getEnd())
1213        end = child.getEnd();
1214      if (level < child.getLevel())
1215        level = child.getLevel();
1216
1217      parentNode.add(child);
1218
1219    } //for
1220
1221    parentNode.setLevel(level+1);
1222    parentNode.setStart(begin);
1223    parentNode.setEnd(end);
1224    parentNode.setUserObject(text);
1225    parentNode.createAnnotation(document,
1226                                treeNodeAnnotationType,
1227                                displayedString,
1228                                utteranceStartOffset.longValue());
1229
1230
1231    return parentNode;
1232  }
1233
1234  /**
1235    * Create a parent node for all selected non-terminal nodes
1236    */
1237  protected STreeNode createParentNode(String text, Annotation annot) {
1238    STreeNode  parentNode = new STreeNode(annot);
1239
1240    long level = -1;
1241    for (Iterator i = selection.iterator(); i.hasNext(); ) {
1242      JButton button = (JButton) i.next();
1243      Integer id = new Integer(button.getActionCommand());
1244
1245      STreeNode child = (STreeNode) nonTerminals.get(id);
1246
1247      if (level < child.getLevel())
1248        level = child.getLevel();
1249
1250      parentNode.add(child);
1251    } //for
1252
1253    parentNode.setLevel(level+1);
1254    parentNode.setUserObject(text);
1255
1256    return parentNode;
1257  }
1258
1259
1260  void selectNode(MouseEvent e) {
1261    // try finding the node that's annotated, i.e., the selected button
1262    if (e.getSource() instanceof JButton) {
1263      JButton source = (JButton) e.getSource();
1264
1265        selection.add(source);
1266        buttonBackground = source.getBackground();
1267        source.setBackground(selectedNodeColor);
1268    }
1269  }
1270
1271  // remove that node from the syntax tree
1272  void removeNode(JButton button) {
1273
1274    Integer id = new Integer(button.getActionCommand());
1275    STreeNode node = (STreeNode) nonTerminals.get(id);
1276    nonTerminals.remove(node);
1277    node.removeAnnotation(document);
1278
1279    //fix the STreeNodes involved
1280    resetChildren(node);
1281    removeNodesAbove(node);
1282
1283    //remove button from everywhere
1284    buttons.remove(button);
1285    button.setVisible(false);
1286    this.remove(button);
1287
1288    //recalculate all lines
1289    recalculateLines();
1290
1291    //make sure we clear the selection
1292    selection.clear();
1293    repaint();
1294  }
1295
1296  //set parent node to null for all children of the given node
1297  private void resetChildren(STreeNode node) {
1298    for (Enumeration e = node.children(); e.hasMoreElements(); )
1299      ((STreeNode) e.nextElement()).setParent(null);
1300
1301    node.disconnectChildren();
1302  }
1303
1304  private void removeNodesAbove(STreeNode node) {
1305    STreeNode parent = (STreeNode) node.getParent();
1306
1307    while (parent != null) {
1308      Integer id = new Integer(parent.getID());
1309      parent.removeAnnotation(document);
1310      if (parent.isNodeChild(node))
1311        parent.remove(node);
1312      parent.disconnectChildren();
1313
1314      nonTerminals.remove(id);
1315
1316      JButton button = (JButton) buttons.get(id);
1317      this.remove(button);
1318      buttons.remove(id);
1319
1320      parent = (STreeNode) parent.getParent();
1321    }
1322  }
1323
1324  private void recalculateLines() {
1325    lines.clear();
1326    //go through all non-terminals and recalculate their lines to their children
1327    for (Iterator i = nonTerminals.values().iterator(); i.hasNext(); )
1328      recalculateLines((STreeNode) i.next());
1329
1330  }
1331
1332  /**
1333    * recalculates all lines from that node to all its children
1334    */
1335  private void recalculateLines(STreeNode node) {
1336    Integer id = new Integer(node.getID());
1337    JButton button = (JButton) buttons.get(id);
1338
1339    int bX = button.getX() + button.getWidth()/2;
1340    int bY = button.getY() + button.getHeight();
1341
1342    for (Enumeration e = node.children(); e.hasMoreElements(); ) {
1343      STreeNode subNode = (STreeNode) e.nextElement();
1344      Integer sid = new Integer(subNode.getID());
1345      JButton subButton = (JButton) buttons.get(sid);
1346
1347      Coordinates coords = new Coordinates(
1348                                bX,
1349                                bY,
1350                                subButton.getX() + subButton.getWidth()/2,
1351                                subButton.getY());
1352
1353      lines.add(coords);
1354    }
1355
1356  }
1357
1358/*
1359  // discontinued from use,done automatically instead, when the utterance is set
1360
1361  public void setTreeAnnotations(AnnotationSet newTreeAnnotations) {
1362    AnnotationSet  oldTreeAnnotations = treeAnnotations;
1363    treeAnnotations = newTreeAnnotations;
1364    firePropertyChange("treeAnnotations", oldTreeAnnotations,
1365                          newTreeAnnotations);
1366  }
1367*/
1368
1369  public void setTreeNodeAnnotationType(String newTreeNodeAnnotationType) {
1370    treeNodeAnnotationType = newTreeNodeAnnotationType;
1371  }
1372
1373  public void setTokenType(String newTokenType) {
1374    if (newTokenType != null && ! newTokenType.equals(""))
1375      tokenType = newTokenType;
1376  }
1377
1378  public String getTokenType() {
1379    return tokenType;
1380  }
1381
1382  void this_componentShown(ComponentEvent e) {
1383    Out.println("Tree Viewer shown");
1384  }
1385
1386  void this_componentHidden(ComponentEvent e) {
1387    Out.println("Tree Viewer closes");
1388  }
1389
1390/*
1391  //None of this works, damn!!!
1392
1393  public void setVisible(boolean b) {
1394    if (!b && this.isVisible())
1395      Out.println("Tree Viewer closes");
1396
1397    super.setVisible( b);
1398  }
1399  public void hide() {
1400    Out.println("Tree Viewer closes");
1401    super.hide();
1402  }
1403*/
1404
1405
1406}// class SyntaxTreeViewer
1407
1408
1409class FocusButton extends JButton {
1410
1411  public FocusButton(String text) {
1412    super(text);
1413  }
1414
1415  public FocusButton() {
1416    super();
1417  }
1418
1419  public FocusButton(Icon icon) {
1420    super(icon);
1421  }
1422
1423  public FocusButton(String text, Icon icon) {
1424    super(text, icon);
1425  }// public FocusButton
1426
1427  public boolean isManagingFocus() {
1428    return true;
1429  }// public boolean isManagingFocus()
1430
1431  public void processComponentKeyEvent(KeyEvent e) {
1432    super.processComponentKeyEvent(e);
1433
1434    //I need that cause I get all events here, so I only want to process
1435    //when it's a release event. The reason is that for keys like <DEL>
1436    //key_typed never happens
1437    if (e.getID() != KeyEvent.KEY_RELEASED)
1438      return;
1439
1440    if (e.getKeyCode() == KeyEvent.VK_DELETE) {
1441      SyntaxTreeViewer viewer = (SyntaxTreeViewer) ((JButton) e.getSource()).getParent();
1442      viewer.removeNode((JButton) e.getSource());
1443    }
1444  }// public void processComponentKeyEvent(KeyEvent e)
1445
1446} // class SyntaxTreeViewer
1447
1448// $Log: SyntaxTreeViewer.java,v $
1449// Revision 1.21  2002/03/06 17:15:46  kalina
1450// Reorganised the source code, so that it now uses constants from
1451// ANNIEConstants, GateConstants and parameter constants defined on each PR.
1452// Read e-mail to the gate list for an explanation.
1453//
1454// Revision 1.20  2001/08/08 16:14:26  kalina
1455// A minor change to the tree viewer.
1456//
1457// Revision 1.19  2001/08/08 14:39:00  kalina
1458// Made the dialog to size itself maximum as much as the screen, coz was
1459// getting too big without that.
1460//
1461// Some documentation on Tree Viewer and some small changes to utterance2trees()
1462// to make it order the tokens correctly by offset
1463//
1464// Revision 1.18  2001/08/07 19:03:05  kalina
1465// Made the tree viewer use Token annotations to break the sentence for annotation
1466//
1467// Revision 1.17  2001/08/07 17:01:32  kalina
1468// Changed the AVR implementing classes in line with the updated AVR
1469// API (cancelAction() and setSpan new parameter).
1470//
1471// Also updated the TreeViewer, so now it can be used to edit and view
1472// Sentence annotations and the SyntaxTreeNodes associated with them.
1473// So if you have trees, it'll show them, if not, it'll help you build them.
1474//
1475// Revision 1.16  2001/04/09 10:36:36  oana
1476// a few changes in the code style
1477//
1478// Revision 1.14  2000/12/04 12:29:29  valyt
1479// Done some work on the visual resources
1480// Added the smart XJTable
1481//
1482// Revision 1.13  2000/11/08 16:35:00  hamish
1483// formatting
1484//
1485// Revision 1.12  2000/10/26 10:45:26  oana
1486// Modified in the code style
1487//
1488// Revision 1.11  2000/10/24 10:10:18  valyt
1489// Fixed the deprecation warning in gate/gui/SyntaxTreeViewer.java
1490//
1491// Revision 1.10  2000/10/18 13:26:47  hamish
1492// Factory.createResource now working, with a utility method that uses reflection (via java.beans.Introspector) to set properties on a resource from the
1493//     parameter list fed to createResource.
1494//     resources may now have both an interface and a class; they are indexed by interface type; the class is used to instantiate them
1495//     moved createResource from CR to Factory
1496//     removed Transients; use Factory instead
1497//
1498// Revision 1.9  2000/10/16 16:44:32  oana
1499// Changed the comment of DEBUG variable
1500//
1501// Revision 1.8  2000/10/10 15:36:35  oana
1502// Changed System.out in Out and System.err in Err;
1503// Added the DEBUG variable seted on false;
1504// Added in the header the licence;
1505//
1506// Revision 1.7  2000/10/10 09:49:57  valyt
1507// Fixed the Annotation test
1508//
1509// Revision 1.6  2000/10/02 12:34:06  valyt
1510// Added the UnicodeEnabled switch on gate.util.Tools
1511//
1512// Revision 1.5  2000/09/28 14:26:09  kalina
1513// Added even more documentation (is this me?!) and allowed several tokens to be
1514// passed instead of a whole utterance/sentence for annotation. Needs good testing this
1515// but will do it when somebody tries using this functionality.
1516//
1517// Revision 1.4  2000/09/28 13:16:12  kalina
1518// Added some documentation
1519//
1520// Revision 1.3  2000/09/21 14:23:45  kalina
1521// Fixed some small bug in main(). To test just run the component itself.
1522//
1523// Revision 1.2  2000/09/21 14:17:27  kalina
1524// Added Unicode support
1525//
1526// Revision 1.1  2000/09/20 17:03:37  kalina
1527// Added the tree viewer from the prototype. It works now with the new annotation API.
1528