1   /*
2    *  Copyright (c) 1998-2001, The University of Sheffield.
3    *
4    *  This file is part of GATE (see http://gate.ac.uk/), and is free
5    *  software, licenced under the GNU Library General Public License,
6    *  Version 2, June 1991 (in the distribution as file licence.html,
7    *  and also available at http://gate.ac.uk/gate/licence.html).
8    *
9    *  Valentin Tablan 13/11/2000
10   *
11   *  $Id: DocumentEditor.java,v 1.49 2001/12/03 14:50:58 valyt Exp $
12   *
13   */
14  package gate.gui;
15  
16  import gate.*;
17  import gate.util.*;
18  import gate.corpora.TestDocument;
19  import gate.creole.tokeniser.DefaultTokeniser;
20  import gate.creole.*;
21  import gate.event.*;
22  import gate.swing.*;
23  
24  import javax.swing.*;
25  import javax.swing.event.*;
26  import javax.swing.table.*;
27  import javax.swing.text.*;
28  import javax.swing.tree.*;
29  import javax.swing.border.*;
30  
31  import java.awt.*;
32  import java.awt.font.*;
33  import java.awt.event.*;
34  import java.awt.image.BufferedImage;
35  import java.awt.print.*;
36  
37  import java.beans.*;
38  import java.util.*;
39  import java.net.*;
40  import java.io.*;
41  
42  /**
43   * This class implements a viewer/editor for the annotations on a document.
44   * As a viewer, this visual resource will display all the annotations found on
45   * the document. The editor needs to have some data about annotation types in
46   * order to allow the editing of annotations. This data comes from the
47   * {@link gate.creole.AnnotationSchema} objects that are loaded in the Gate
48   * system at a given moment. If there are no such objects the editing of
49   * annotations will be restricted to a very crude method allowing the user to
50   * add any type of annotations having any features with any String values.
51   */
52  public class DocumentEditor extends AbstractVisualResource
53                              implements ANNIEConstants{
54    //properties
55    private transient PropertyChangeSupport propertyChangeListeners =
56                                            new PropertyChangeSupport(this);
57    /**
58     * The {@link gate.Document} currently displayed.
59     */
60    private gate.Document document;
61  
62    /**
63     * A random colour generator used to generate initial default colours for
64     * highlighting various types of annotations.
65     */
66    protected ColorGenerator colGenerator = new ColorGenerator();
67  
68    //GUI components
69    /** The text display.*/
70    protected JTextPane textPane;
71  
72    /** Scroller used for the text diaplay*/
73    protected JScrollPane textScroll;
74  
75    /** The table placed below the text display used for showing annotations*/
76    protected XJTable annotationsTable;
77  
78    /**Model for the annotations table*/
79    protected AnnotationsTableModel annotationsTableModel;
80  
81    /** Scroller for the annotations table*/
82    protected JScrollPane tableScroll;
83  
84    /*The split that contains the text(top) and the annotations table(bottom)*/
85    protected JSplitPane leftSplit;
86  
87    /**
88     * The split that contains the styles tree and the coreference viewer.
89     */
90    protected JSplitPane rightSplit;
91  
92    /**
93     * The main horizontal split that contains all the contents of this viewer
94     */
95    protected JSplitPane mainSplit;
96  
97    /**
98     * The right hand side tree with all  the annotation sets and types of
99     * annotations
100    */
101   protected JTree stylesTree;
102 
103   /**
104    * The toolbar displayed on the top part of the component
105    */
106   protected JToolBar toolbar;
107 
108   /**Scroller for the styles tree*/
109   protected JScrollPane stylesTreeScroll;
110 
111   /**The root for the styles tree*/
112   protected DefaultMutableTreeNode stylesTreeRoot;
113 
114   /**The model for the styles tree*/
115   protected DefaultTreeModel stylesTreeModel;
116 
117   /**The dialog used for editing the styles used to highlight annotations*/
118   protected TextAttributesChooser styleChooser;
119 
120 
121   /**
122    * The Jtree that displays the coreference data
123    */
124   protected JTree corefTree;
125   /**
126    * The root for the coref tree
127    */
128   protected DefaultMutableTreeNode corefTreeRoot;
129 
130   /**
131    * The model for the coref tree
132    */
133   protected DefaultTreeModel corefTreeModel;
134 
135   /** The scroller for the coref list*/
136   protected JScrollPane corefScroll;
137 
138   /**
139    * A box containing a {@link javax.swing.JProgressBar} used to keep the user
140    * entertained while the text display is being updated
141    */
142   protected Box progressBox;
143 
144   /**The progress bar used during updating the text*/
145   protected JProgressBar progressBar;
146 
147   /**
148    * The highlighter used to help the user select annotations that overlap
149    * and for highligting in the text the annotations selected in the lower
150    * table.
151    */
152   protected Highlighter highlighter;
153 
154   /**
155    * This highlighter is actually used as a data structure. It is used to keep
156    * the data for the selected annotations; the actual highlighting will be
157    * done by the {@link AnnotationEditor#highlighter} as using two different
158    * highlighters on the same text component is looking for trouble.
159    */
160   protected Highlighter selectionHighlighter;
161 
162   /**
163    * The object responsible with blinking the selected annotations.
164    */
165   protected SelectionBlinker selectionBlinker;
166 
167 
168   protected Handle myHandle;
169 
170   /**
171    * holds the data for the  annotations table: a list of Annotation objects
172    */
173   protected java.util.List data;
174 
175   /**
176    * a list containing {@link AnnotationEditor.Range} objects. These are the
177    * ranges in the {@link AnnotationEditor#data} structure. A range is a bunch
178    * of annotations belonging to the same annotation set that are contiguous
179    * in the {@link AnnotationEditor#data} structure.
180    */
181   protected java.util.List ranges;
182 
183   /**
184    * A composed map used to get the metadata for an annotation type starting
185    * from the annotation set name and the type name.
186    * Annotation set name -> Annotation type -> {@link AnnotationEditor.TypeData}
187    * Maps from String to Map to {@link AnnotationEditor.TypeData}.
188    */
189   protected Map typeDataMap;
190 
191   /**
192    * The listener for the events coming from the document (annotations and
193    * annotation sets added or removed).
194    */
195   protected EventsHandler eventHandler;
196 
197 
198   /**
199    * Object used to sychronise all the various threads involved in GUI
200    * updating;
201    */
202   protected Object lock;
203 
204   /**Should the table be visible*/
205 
206   /**Should the text be visible*/
207 
208   /**
209    * Should the right hand side tree be visible. That tree is used to select
210    * what types of annotations are visible in the text display, hence the name
211    * filters.
212    */
213 
214   /**Should this component bahave as an editor as well as an viewer*/
215   private boolean editable = true;
216 
217 
218 
219   private JToggleButton textVisibleBtn;
220   private JToggleButton typesTreeVisibleBtn;
221   private JToggleButton annotationsTableVisibleBtn;
222   private JToggleButton coreferenceVisibleBtn;
223   private boolean annotationsTableVisible = false;
224   private boolean coreferenceVisible = false;
225   private boolean textVisible = true;
226   private boolean typesTreeVisible = false;
227   private boolean corefOptionAvailable = false;
228 
229   /**
230    * Default constructor. Creats all the components and initialises all the
231    * internal data to default values where possible.
232    */
233   public DocumentEditor() {
234   }
235 
236   public Resource init(){
237     initLocalData();
238     initGuiComponents();
239     initListeners();
240     return this;
241   }
242 
243   /** Test code*/
244   public static void main(String[] args) {
245     try{
246       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
247       Gate.setLocalWebServer(false);
248       Gate.setNetConnected(false);
249       Gate.init();
250       JFrame frame = new JFrame("Gate Document Editor Test");
251       frame.addWindowListener(new WindowAdapter(){
252         public void windowClosing(WindowEvent e){
253           System.exit(0);
254         }
255       });
256 
257       //get a document
258       FeatureMap params = Factory.newFeatureMap();
259       params.put("markupAware", new Boolean(true));
260 
261       params.put("sourceUrl",
262                  "file:///d:/tmp/help-doc.html");
263                  //"file:///d:/tmp/F7V.xml");
264                  //"http://redmires.dcs.shef.ac.uk/admin/index.html");
265                  //"http://redmires.dcs.shef.ac.uk/java1.3docs/api/javax/
266                  //                                       swing/Action.html");
267                  //"http://redmires.dcs.shef.ac.uk/java1.3docs/api/java/awt
268                  ///AWTEventMulticaster.html");
269       gate.Document doc = (gate.Document)Factory.createResource(
270                                           "gate.corpora.DocumentImpl", params);
271       //create a default tokeniser
272      params.clear();
273      params.put("rulesResourceName", "creole/tokeniser/DefaultTokeniser.rules");
274      DefaultTokeniser tokeniser = (DefaultTokeniser) Factory.createResource(
275                             "gate.creole.tokeniser.DefaultTokeniser", params);
276 
277       tokeniser.setDocument(doc);
278       tokeniser.setAnnotationSetName("TokeniserAS");
279       tokeniser.execute();
280 
281       DocumentEditor editor = (DocumentEditor)Factory.createResource(
282                             "gate.gui.DocumentEditor", Factory.newFeatureMap());
283       frame.getContentPane().add(editor);
284       frame.pack();
285       frame.setVisible(true);
286       editor.setTarget(doc);
287 
288       //get the annotation schemas
289       params =  Factory.newFeatureMap();
290       params.put("xmlFileUrl", DocumentEditor.class.getResource(
291                               "/gate/resources/creole/schema/PosSchema.xml"));
292 
293       AnnotationSchema annotSchema = (AnnotationSchema)
294          Factory.createResource("gate.creole.AnnotationSchema", params);
295       Set annotationSchemas = new HashSet();
296       annotationSchemas.add(annotSchema);
297 
298     }catch(Exception e){
299       e.printStackTrace(Err.getPrintWriter());
300     }
301   }//public static void main(String[] args)
302 
303 
304   /**
305    * Initialises all the listeners that this component has to register with
306    * other classes.
307    */
308   protected void initListeners() {
309     //listen for our own properties change events
310     this.addPropertyChangeListener(new PropertyChangeListener() {
311       public void propertyChange(PropertyChangeEvent e) {
312         if(e.getPropertyName().equals("annotationsTableVisible") ||
313            e.getPropertyName().equals("coreferenceVisible") ||
314            e.getPropertyName().equals("textVisible") ||
315            e.getPropertyName().equals("typesTreeVisible")){
316           layoutComponents();
317         }else if(e.getPropertyName().equals("corefOptionAvailable")){
318           if(((Boolean)e.getNewValue()).booleanValue()){
319             if(toolbar.getComponentIndex(coreferenceVisibleBtn) == -1)
320               toolbar.add(coreferenceVisibleBtn,
321                           toolbar.getComponentCount() - 1);
322           }else{
323             toolbar.remove(coreferenceVisibleBtn);
324           }
325           layoutComponents();
326         }
327       }
328     });
329 
330     textVisibleBtn.addActionListener(new ActionListener() {
331       public void actionPerformed(ActionEvent e) {
332         setTextVisible(textVisibleBtn.isSelected());
333       }
334     });
335 
336     annotationsTableVisibleBtn.addActionListener(new ActionListener() {
337       public void actionPerformed(ActionEvent e) {
338         setAnnotationsTableVisible(annotationsTableVisibleBtn.isSelected());
339       }
340     });
341 
342 
343     typesTreeVisibleBtn.addActionListener(new ActionListener() {
344       public void actionPerformed(ActionEvent e) {
345         setTypesTreeVisible(typesTreeVisibleBtn.isSelected());
346       }
347     });
348 
349 
350     coreferenceVisibleBtn.addActionListener(new ActionListener() {
351       public void actionPerformed(ActionEvent e) {
352         setCoreferenceVisible(coreferenceVisibleBtn.isSelected());
353       }
354     });
355 
356     stylesTree.addMouseListener(new MouseAdapter() {
357       public void mouseClicked(MouseEvent e) {
358         if(SwingUtilities.isLeftMouseButton(e)){
359           //where inside the tree?
360           int x = e.getX();
361           int y = e.getY();
362           TreePath path = stylesTree.getPathForLocation(x, y);
363           if(path != null){
364             DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.
365                                          getLastPathComponent();
366             TypeData nData = (TypeData)node.getUserObject();
367             //where inside the cell?
368             Rectangle cellRect = stylesTree.getPathBounds(path);
369             x -= cellRect.x;
370             y -= cellRect.y;
371             Component cellComp = stylesTree.getCellRenderer().
372                                  getTreeCellRendererComponent(stylesTree,
373                                                               node, true,
374                                                               false, false,
375                                                               0, true);
376 //            cellComp.setSize(cellRect.width, cellRect.height);
377             cellComp.setBounds(cellRect);
378             Component clickedComp = cellComp.getComponentAt(x, y);
379 
380             if(clickedComp instanceof JCheckBox){
381               nData.setVisible(! nData.getVisible());
382 //              stylesTree.repaint(cellRect);
383               stylesTreeModel.nodeChanged(node);
384             // Check if the click indicates a shortcut to create an annotation
385             }else if( e.getClickCount() == 1 &&
386                       clickedComp instanceof JLabel &&
387                       isTextSelected()){
388               // Here creates an annotation with the selected text into the
389               // target annotation set
390 
391               if(!editable) return;
392               Long startOffset = new Long(textPane.getSelectionStart());
393               Long endOffset = new Long(textPane.getSelectionEnd());
394               TreePath treePath = stylesTree.getSelectionPath();
395               TypeData typeData = (TypeData)((DefaultMutableTreeNode)
396                               treePath.getLastPathComponent()).getUserObject();
397               String setName = typeData.getSet();
398               if(typeData.getType() == null){
399                 // The set is empty. It will not create an annotation.
400                 // Loose the selection and return
401                 textPane.setSelectionStart(startOffset.intValue());
402                 textPane.setSelectionEnd(startOffset.intValue());
403                 return;
404               }// End if
405               try{
406                 if ("Default".equals(setName)){
407                   document.getAnnotations().add(startOffset,
408                                                 endOffset,
409                                                 typeData.getType(),
410                                                 Factory.newFeatureMap());
411                 }else{
412                   document.getAnnotations(setName).add( startOffset,
413                                                         endOffset,
414                                                         typeData.getType(),
415                                                        Factory.newFeatureMap());
416                 }// End if
417               } catch(gate.util.InvalidOffsetException ioe){
418                 throw new GateRuntimeException(ioe.getMessage());
419               }// End try
420               // Loose the selection
421               textPane.setSelectionStart(startOffset.intValue());
422               textPane.setSelectionEnd(startOffset.intValue());
423             }else if(clickedComp instanceof JLabel &&
424                      e.getClickCount() == 2){
425               if(styleChooser == null){
426                 Window parent = SwingUtilities.getWindowAncestor(
427                                   DocumentEditor.this);
428                 styleChooser = parent instanceof Frame ?
429                                new TextAttributesChooser((Frame)parent,
430                                                          "Please select your options",
431                                                          true) :
432                                new TextAttributesChooser((Dialog)parent,
433                                                          "Please select your options",
434                                                          true);
435 
436               }
437 
438               styleChooser.setLocationRelativeTo(stylesTree);
439               nData.setAttributes(
440                     styleChooser.show(nData.getAttributes().copyAttributes()));
441               stylesTreeModel.nodeChanged(node);
442 //              stylesTree.repaint(cellRect);
443             }
444           }
445         }
446       }
447     });
448 
449     stylesTree.addComponentListener(new ComponentAdapter() {
450       public void componentHidden(ComponentEvent e) {
451 
452       }
453 
454       public void componentMoved(ComponentEvent e) {
455       }
456 
457       public void componentResized(ComponentEvent e) {
458         SwingUtilities.invokeLater(new Runnable(){
459           public void run(){
460             Enumeration nodes = stylesTreeRoot.depthFirstEnumeration();
461             while(nodes.hasMoreElements()){
462               stylesTreeModel.nodeChanged((TreeNode)nodes.nextElement());
463             }
464           }
465         });
466       }
467 
468       public void componentShown(ComponentEvent e) {
469       }
470     });
471 
472     //clear selection in table on outside clicks
473     tableScroll.addMouseListener(new MouseAdapter() {
474       public void mouseClicked(MouseEvent e) {
475         Point location = e.getPoint();
476         if(!tableScroll.getViewport().getView().getBounds().contains(location)){
477           //deselect everything in the table
478           annotationsTable.clearSelection();
479         }
480       }
481     });
482 
483     annotationsTable.addMouseListener(new MouseAdapter() {
484       public void mouseClicked(MouseEvent e) {
485         int row = annotationsTable.rowAtPoint(e.getPoint());
486         Annotation ann = (Annotation)annotationsTable.getModel().
487                                                       getValueAt(row, -1);
488         //find the annotation set
489         String setName = (String)annotationsTable.getModel().
490                                                     getValueAt(row, 1);
491         AnnotationSet set = setName.equals("Default")?
492                             document.getAnnotations() :
493                             document.getAnnotations(setName);
494 
495         EditAnnotationAction editAnnAct = new EditAnnotationAction(set, ann);
496         if(SwingUtilities.isLeftMouseButton(e)){
497           if(e.getClickCount() == 1){
498           }else if(e.getClickCount() == 2){
499             //double left click -> edit the annotation
500             if(editable) editAnnAct.actionPerformed(null);
501           }
502         } else if(SwingUtilities.isRightMouseButton(e)) {
503           //right click
504           //add select all option
505           JPopupMenu popup = new JPopupMenu();
506           popup.add(new AbstractAction(){
507             {
508               putValue(NAME, "Select all");
509             }
510             public void actionPerformed(ActionEvent evt){
511               annotationsTable.selectAll();
512             }
513           });
514 
515           popup.addSeparator();
516           //add save as XML and preserve format
517           popup.add(new DumpAsXmlAction());
518           if(editable){
519             //add delete option
520             popup.addSeparator();
521             popup.add(new DeleteSelectedAnnotationsAction(annotationsTable));
522             popup.addSeparator();
523             popup.add(new XJMenuItem(editAnnAct, myHandle));
524           }
525           popup.show(annotationsTable, e.getX(), e.getY());
526         }
527       }
528     });//annotationsTable.addMouseListener(new MouseAdapter()
529 
530 
531 
532     annotationsTable.getInputMap().put(
533                   KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0),
534                   "Delete");
535     annotationsTable.getActionMap().put(
536                         "Delete",
537                         new DeleteSelectedAnnotationsAction(annotationsTable));
538 
539     stylesTree.getInputMap().put(
540                   KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0),
541                   "Delete");
542     stylesTree.getActionMap().put(
543                         "Delete",
544                         new DeleteSelectedAnnotationsAction(stylesTree));
545 
546     //takes care of highliting the selected annotations
547     annotationsTable.getSelectionModel().addListSelectionListener(
548       new ListSelectionListener(){
549         public void valueChanged(ListSelectionEvent e){
550           int[] rows = annotationsTable.getSelectedRows();
551           synchronized(selectionHighlighter){
552             selectionHighlighter.removeAllHighlights();
553           }
554           for(int i = 0; i < rows.length; i++){
555             int start = ((Long)annotationsTable.getModel().
556                          getValueAt(rows[i], 2)
557                         ).intValue();
558             int end = ((Long)annotationsTable.getModel().
559                        getValueAt(rows[i], 3)
560                       ).intValue();
561             //bring the annotation in view
562             try{
563               Rectangle startRect = textPane.modelToView(start);
564               Rectangle endRect = textPane.modelToView(end);
565               SwingUtilities.computeUnion(endRect.x, endRect.y,
566                                           endRect.width, endRect.height,
567                                           startRect);
568               textPane.scrollRectToVisible(startRect);
569               annotationsTable.requestFocus();
570             }catch(BadLocationException ble){
571               throw new GateRuntimeException(ble.toString());
572             }
573             //start blinking the annotation
574             try{
575               synchronized (selectionHighlighter){
576                 selectionHighlighter.addHighlight(start, end,
577                             DefaultHighlighter.DefaultPainter);
578               }
579             }catch(BadLocationException ble){
580               throw new GateRuntimeException(ble.toString());
581             }
582           }//for(int i = 0; i < rows.length; i++)
583           //start the blinker
584           selectionBlinker.testAndStart();
585         }
586       });
587 
588 
589     textPane.addMouseListener(new MouseAdapter() {
590       public void mouseClicked(MouseEvent e) {
591         if(SwingUtilities.isRightMouseButton(e)){
592           int position = textPane.viewToModel(e.getPoint());
593           if(textPane.getSelectionStart() ==  textPane.getSelectionEnd()){
594             //no selection -> select an annotation
595             JPopupMenu popup = new JPopupMenu("Select:");
596             //find annotations at this position
597             Iterator annIter = document.getAnnotations().
598                                         get(new Long(position),
599                                             new Long(position)
600                                         ).iterator();
601             if(annIter.hasNext()){
602               JMenu menu = new JMenu("Default");
603               popup.add(menu);
604               while(annIter.hasNext()){
605                 Annotation ann = (Annotation)annIter.next();
606                 JMenuItem item = new SelectAnnotationPopupItem(ann,
607                                                                   "Default");
608                 menu.add(item);
609               }
610             }
611             Map namedASs = document.getNamedAnnotationSets();
612             if(namedASs != null){
613               Iterator namedASiter = namedASs.values().iterator();
614               while(namedASiter.hasNext()){
615                 //find annotations at this position
616                 AnnotationSet set = (AnnotationSet)namedASiter.next();
617                 annIter = set.get(new Long(position), new Long(position)).
618                               iterator();
619                 if(annIter.hasNext()){
620                   JMenu menu = new JMenu(set.getName());
621                   popup.add(menu);
622                   while(annIter.hasNext()){
623                     Annotation ann = (Annotation)annIter.next();
624                     JMenuItem item = new SelectAnnotationPopupItem(ann,
625                                                                 set.getName());
626                     menu.add(item);
627                   }
628                 }
629               }
630             }
631             popup.show(textPane, e.getPoint().x, e.getPoint().y);
632           } else {
633             //there is selected text -> create a new annotation
634             if(!editable) return;
635             Long startOffset = new Long(textPane.getSelectionStart());
636             Long endOffset = new Long(textPane.getSelectionEnd());
637             JPopupMenu popup = new JPopupMenu();
638             //add new annotation in the Default AS
639             JMenu menu = new JMenu("Add annotation to \"Default\"");
640             menu.add(new XJMenuItem(
641                          new NewAnnotationAction(document.getAnnotations(),
642                                                  startOffset, endOffset),
643                          myHandle));
644             java.util.List customisedAnnTypes = Gate.getCreoleRegister().
645                                                 getVREnabledAnnotationTypes();
646             if(!customisedAnnTypes.isEmpty()){
647               menu.addSeparator();
648               Iterator typesIter = customisedAnnTypes.iterator();
649               while(typesIter.hasNext()){
650                 menu.add(new XJMenuItem(
651                              new NewAnnotationAction(document.getAnnotations(),
652                                                      (String)typesIter.next(),
653                                                      startOffset, endOffset),
654                              myHandle));
655               }
656             }//if(!customisedAnnTypes.isEmpty())
657             popup.add(menu);
658 
659             //add a new annotation to a named AnnotationSet
660             if(document.getNamedAnnotationSets() != null){
661               Iterator annSetsIter = document.getNamedAnnotationSets().
662                                               keySet().iterator();
663               if(annSetsIter.hasNext()) popup.addSeparator();
664               while(annSetsIter.hasNext()){
665                 AnnotationSet set = document.getAnnotations(
666                                              (String)annSetsIter.next());
667 
668 
669                 menu = new JMenu("Add annotation to \"" + set.getName() + "\"");
670                 menu.add(new XJMenuItem(
671                              new NewAnnotationAction(set, startOffset, endOffset),
672                              myHandle));
673                 if(!customisedAnnTypes.isEmpty()){
674                   menu.addSeparator();
675                   Iterator typesIter = customisedAnnTypes.iterator();
676                   while(typesIter.hasNext()){
677                     menu.add(new XJMenuItem(
678                                  new NewAnnotationAction(set,
679                                                          (String)typesIter.next(),
680                                                          startOffset, endOffset),
681                                  myHandle));
682                   }
683                 }//if(!customisedAnnTypes.isEmpty())
684                 popup.add(menu);
685               }//while(annSetsIter.hasNext())
686             }
687 
688             //add to a new annotation set
689             menu = new JMenu("Add annotation to a new set");
690             menu.add(new XJMenuItem(
691                          new NewAnnotationAction(null, startOffset, endOffset),
692                          myHandle));
693             if(!customisedAnnTypes.isEmpty()){
694               menu.addSeparator();
695               Iterator typesIter = customisedAnnTypes.iterator();
696               while(typesIter.hasNext()){
697                 menu.add(new XJMenuItem(
698                              new NewAnnotationAction(null,
699                                                      (String)typesIter.next(),
700                                                      startOffset, endOffset),
701                              myHandle));
702               }
703             }//if(!customisedAnnTypes.isEmpty())
704             popup.add(menu);
705             //show the popup
706             popup.show(textPane, e.getPoint().x, e.getPoint().y);
707           }//there is selected text
708         }//if(SwingUtilities.isRightMouseButton(e))
709       }//mouse clicked
710 
711       public void mousePressed(MouseEvent e) {
712       }
713 
714       public void mouseReleased(MouseEvent e) {
715       }
716 
717       public void mouseEntered(MouseEvent e) {
718       }
719 
720       public void mouseExited(MouseEvent e) {
721       }
722     });
723 
724     //when the highlighter changes we need to get a hold of the new one
725     textPane.addPropertyChangeListener(new PropertyChangeListener() {
726       public void propertyChange(PropertyChangeEvent e) {
727         if(e.getPropertyName().equals("highlighter")){
728           highlighter = textPane.getHighlighter();
729           selectionHighlighter.install(textPane);
730         }
731       }
732     });
733 
734     corefTree.addMouseListener(new MouseAdapter() {
735       public void mouseClicked(MouseEvent e) {
736         if(SwingUtilities.isLeftMouseButton(e)){
737           //where inside the tree?
738           int x = e.getX();
739           int y = e.getY();
740           TreePath path = corefTree.getPathForLocation(x, y);
741           if(path != null){
742             DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.
743                                          getLastPathComponent();
744             if(node.getUserObject() instanceof CorefData){
745               CorefData cData = (CorefData)node.getUserObject();
746               cData.setVisible(!cData.getVisible());
747               corefTreeModel.nodeChanged(node);
748             }
749           }
750         }
751       }
752 
753       public void mousePressed(MouseEvent e) {
754       }
755 
756       public void mouseReleased(MouseEvent e) {
757       }
758 
759       public void mouseEntered(MouseEvent e) {
760       }
761 
762       public void mouseExited(MouseEvent e) {
763       }
764     });
765 
766 
767 
768     corefTree.addComponentListener(new ComponentAdapter() {
769       public void componentHidden(ComponentEvent e) {
770 
771       }
772 
773       public void componentMoved(ComponentEvent e) {
774       }
775 
776       public void componentResized(ComponentEvent e) {
777         SwingUtilities.invokeLater(new Runnable(){
778           public void run(){
779             Enumeration nodes = corefTreeRoot.depthFirstEnumeration();
780             while(nodes.hasMoreElements()){
781               corefTreeModel.nodeChanged((TreeNode)nodes.nextElement());
782             }
783           }
784         });
785       }
786 
787       public void componentShown(ComponentEvent e) {
788       }
789     });
790   }//protected void initListeners()
791 
792   /**
793    * Initialises the local variables to their default values
794    */
795   protected void initLocalData(){
796     //init local vars
797     lock = new Object();
798 
799     data = Collections.synchronizedList(new ArrayList());
800     //dataAsAS = new gate.annotation.AnnotationSetImpl(document);
801     ranges = new ArrayList();
802 
803     typeDataMap = new HashMap();
804 
805     eventHandler = new EventsHandler();
806 
807   }//protected void initLocalData()
808 
809   /**Builds all the graphical components*/
810   protected void initGuiComponents(){
811     //initialise GUI components
812     this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
813 
814     //the toolbar
815     toolbar = new JToolBar(JToolBar.HORIZONTAL);
816     toolbar.setAlignmentX(Component.LEFT_ALIGNMENT);
817     toolbar.setAlignmentY(Component.TOP_ALIGNMENT);
818     toolbar.setFloatable(false);
819     this.add(toolbar);
820 
821     textVisibleBtn = new JToggleButton("Text", textVisible);
822     toolbar.add(textVisibleBtn);
823 
824     annotationsTableVisibleBtn = new JToggleButton("Annotations",
825                                                    annotationsTableVisible);
826     toolbar.add(annotationsTableVisibleBtn);
827 
828     typesTreeVisibleBtn = new JToggleButton("Annotation Sets", typesTreeVisible);
829     toolbar.add(typesTreeVisibleBtn);
830 
831 
832     coreferenceVisibleBtn = new JToggleButton("Coreference", coreferenceVisible);
833     if(isCorefOptionAvailable()) toolbar.add(coreferenceVisibleBtn);
834 
835 
836     //printing
837 //    toolbar.add(Box.createHorizontalStrut(20));
838 //    toolbar.add(new PrintAction());
839 
840 
841 
842     toolbar.add(Box.createHorizontalGlue());
843 
844 
845     //The text
846     textPane = new XJTextPane();
847     textPane.setEditable(false);
848     textPane.setEnabled(true);
849     textPane.setEditorKit(new CustomStyledEditorKit());
850     Style defaultStyle = textPane.getStyle("default");
851     StyleConstants.setBackground(defaultStyle, Color.white);
852     StyleConstants.setFontFamily(defaultStyle, "Arial Unicode MS");
853     textScroll = new JScrollPane(textPane);
854     textScroll.setAlignmentY(Component.TOP_ALIGNMENT);
855     textScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
856 
857 
858     //The table
859     annotationsTableModel = new AnnotationsTableModel();
860     annotationsTable = new XJTable(annotationsTableModel);
861     annotationsTable.setIntercellSpacing(new Dimension(10, 5));
862 
863     tableScroll = new JScrollPane(annotationsTable);
864     tableScroll.setOpaque(true);
865     tableScroll.setAlignmentY(Component.TOP_ALIGNMENT);
866     tableScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
867 
868 
869     //RIGHT SIDE - the big tree
870     stylesTreeRoot = new DefaultMutableTreeNode(null, true);
871     stylesTreeModel = new DefaultTreeModel(stylesTreeRoot, true);
872     stylesTree = new JTree(stylesTreeModel){
873       public void updateUI(){
874         super.updateUI();
875         setRowHeight(0);
876       }
877     };
878 
879     stylesTree.setRootVisible(false);
880     stylesTree.setCellRenderer(new NodeRenderer());
881     //TIP: setting rowHeight to 0 tells the tree to query its renderer for each
882     //row's size
883     stylesTree.setRowHeight(0);
884     stylesTree.setShowsRootHandles(true);
885     stylesTree.setToggleClickCount(0);
886     stylesTreeScroll = new JScrollPane(stylesTree);
887     stylesTreeScroll.setAlignmentY(Component.TOP_ALIGNMENT);
888     stylesTreeScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
889 
890 
891     //coreference
892     corefTreeRoot = new DefaultMutableTreeNode("Coreference data", true);
893     corefTree = new JTree(corefTreeModel = new DefaultTreeModel(corefTreeRoot,
894                                                                 true));
895     corefTree.setCellRenderer(new CorefNodeRenderer());
896     corefTree.setRowHeight(0);
897     corefTree.setRootVisible(true);
898     corefTree.setShowsRootHandles(false);
899     corefScroll = new JScrollPane(corefTree);
900     corefScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
901     corefScroll.setAlignmentY(Component.TOP_ALIGNMENT);
902     updateCorefTree();
903 
904     //various containers
905     leftSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, false);
906     leftSplit.setOneTouchExpandable(true);
907     leftSplit.setOpaque(true);
908     leftSplit.setAlignmentY(Component.TOP_ALIGNMENT);
909     leftSplit.setAlignmentX(Component.LEFT_ALIGNMENT);
910     leftSplit.setResizeWeight((double)0.75);
911 
912     rightSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, false);
913     rightSplit.setOneTouchExpandable(true);
914     rightSplit.setOpaque(true);
915     rightSplit.setAlignmentY(Component.TOP_ALIGNMENT);
916     rightSplit.setAlignmentX(Component.LEFT_ALIGNMENT);
917     rightSplit.setResizeWeight((double)0.75);
918 
919 
920     mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, false);
921     mainSplit.setOneTouchExpandable(true);
922     mainSplit.setOpaque(true);
923     mainSplit.setAlignmentY(Component.TOP_ALIGNMENT);
924     mainSplit.setAlignmentX(Component.LEFT_ALIGNMENT);
925     mainSplit.setResizeWeight((double)0.75);
926 
927     //put everything together
928     layoutComponents();
929 
930     //Extra Stuff
931 
932     progressBox = new Box(BoxLayout.X_AXIS);
933     progressBox.add(Box.createHorizontalStrut(5));
934     progressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);
935     progressBox.add(progressBar);
936     progressBox.add(Box.createHorizontalStrut(5));
937 
938     highlighter = textPane.getHighlighter();
939     if(highlighter instanceof javax.swing.text.DefaultHighlighter){
940       ((javax.swing.text.DefaultHighlighter)highlighter).
941       setDrawsLayeredHighlights(true);
942     }
943 
944     selectionHighlighter = new DefaultHighlighter();
945     selectionHighlighter.install(textPane);
946     selectionBlinker = new SelectionBlinker();
947 
948   }//protected void initGuiComponents()
949 
950 
951   /** This method returns true if a text is selected in the textPane*/
952   private boolean isTextSelected(){
953     return !(textPane.getSelectionStart()==textPane.getSelectionEnd());
954   }// isTextSelected()
955   /**
956    * Gets all the {@link gate.creole.AnnotationSchema} objects currently
957    * loaded in the system.
958    */
959   protected Set getAnnotationSchemas(){
960     Set result = new HashSet();
961     ResourceData rData = (ResourceData)Gate.getCreoleRegister().
962                                             get("gate.creole.AnnotationSchema");
963     if(rData != null){
964       result.addAll(rData.getInstantiations());
965     }
966     return result;
967   }//protected Set getAnnotationSchemas()
968 
969   public synchronized void removePropertyChangeListener(
970                                                     PropertyChangeListener l) {
971     super.removePropertyChangeListener(l);
972     propertyChangeListeners.removePropertyChangeListener(l);
973   }
974 
975   public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
976     super.addPropertyChangeListener(l);
977     propertyChangeListeners.addPropertyChangeListener(l);
978   }
979 
980   public synchronized void addPropertyChangeListener(String propertyName,
981                                                      PropertyChangeListener l) {
982     super.addPropertyChangeListener(propertyName, l);
983     propertyChangeListeners.addPropertyChangeListener(propertyName, l);
984   }
985 
986   /**
987    * Sets the document to be displayed
988    */
989   public void setTarget(Object target){
990     if(!(target instanceof gate.Document)){
991       throw new IllegalArgumentException(
992         "The document editor can only display Gate documents!\n" +
993         "The provided resource is not a document but a: " +
994         target.getClass().toString() + "!");
995     }
996     gate.Document  oldDocument = document;
997     document = (gate.Document)target;
998     //this needs to be executed even if the new document equals(oldDocument)
999     //in order to update the pointers
1000    if(oldDocument != document) this_documentChanged();
1001
1002    propertyChangeListeners.firePropertyChange("document", oldDocument,
1003                                               target);
1004  }//public void setTarget(Object target)
1005
1006  public void setHandle(Handle handle){
1007    myHandle = handle;
1008  }
1009
1010  public void cleanup(){
1011    document = null;
1012    stylesTreeRoot.removeAllChildren();
1013    data.clear();
1014    ranges.clear();
1015  }
1016
1017
1018  /**
1019   * Updates this component when the underlying document is changed. This method
1020   * is only triggered when the document is changed to a new one and not when
1021   * the internal data from the document changes. For the document internal
1022   * events {@see #DelayedListener}.
1023   */
1024  protected void this_documentChanged(){
1025    initLocalData();
1026    annotationsTableModel.fireTableDataChanged();
1027    document.getFeatures().addFeatureMapListener(new FeatureMapListener(){
1028      public void featureMapUpdated(){
1029          updateCorefTree();
1030      }
1031    });
1032    updateCorefTree();
1033
1034    Enumeration enum = stylesTreeRoot.children();
1035    while(enum.hasMoreElements()){
1036      stylesTreeModel.removeNodeFromParent((DefaultMutableTreeNode)
1037                                           enum.nextElement());
1038    }
1039    if(document == null) return;
1040    textPane.setText(document.getContent().toString());
1041
1042    //add the default annotation set
1043    eventHandler.annotationSetAdded(new gate.event.DocumentEvent(
1044                  document,
1045                  gate.event.DocumentEvent.ANNOTATION_SET_ADDED, null));
1046
1047    //register the for this new document's events
1048    document.addDocumentListener(eventHandler);
1049
1050    //add all the other annotation sets
1051    Map namedASs = document.getNamedAnnotationSets();
1052    if(namedASs != null){
1053      Iterator setsIter = namedASs.values().iterator();
1054      while(setsIter.hasNext()){
1055        AnnotationSet currentAS = (AnnotationSet)setsIter.next();
1056        if(currentAS != null){
1057          eventHandler.annotationSetAdded(new gate.event.DocumentEvent(
1058                        document,
1059                        gate.event.DocumentEvent.ANNOTATION_SET_ADDED,
1060                        currentAS.getName()));
1061        }
1062      }
1063    }
1064  }//protected void this_documentChanged()
1065
1066  /**
1067   * Gets the data related to a given annotation type.
1068   * An annotation type is uniquely identified by the name of its AnnotationSet
1069   * and the name of the type.
1070   * For the default annotation set of a document (which has no name) the
1071   * &quot;&lt;Default&gt;&quot; value is used.
1072   *
1073   * Once a {@link AnnotationEditor.TypeData} value has been obtained it can be used to change
1074   * the way the respective type of annotations are displayed.
1075   * @param setName a {@link java.lang.String}, the name of the annotation set
1076   * @param type a {@link java.lang.String}, the name of the type.
1077   * @return a {@link AnnotationEditor.TypeData} value
1078   */
1079  protected TypeData getTypeData(String setName, String type){
1080    Map setMap = (Map)typeDataMap.get(setName);
1081    if(setMap != null) return (TypeData)setMap.get(type);
1082    else return null;
1083  }// protected TypeData getTypeData(String setName, String type)
1084
1085
1086  /**
1087   * Repaints the highlighting for annotation types in the text display.
1088   */
1089  protected void showHighlights(Set annotations, AttributeSet style) {
1090    //store the state of the text display
1091    int selStart = textPane.getSelectionStart();
1092    int selEnd = textPane.getSelectionEnd();
1093    final int position = textPane.viewToModel(
1094                            textScroll.getViewport().getViewPosition());
1095    //hide the text
1096    SwingUtilities.invokeLater(new Runnable() {
1097      public void run() {
1098        progressBar.setValue(0);
1099        //progressBar.setMaximumSize(new Dimension(textScroll.getWidth(),20));
1100        textScroll.getViewport().setView(progressBox);
1101        textScroll.paintImmediately(textScroll.getBounds());
1102      }
1103    });
1104
1105    //highlight the annotations
1106    int size = annotations.size();
1107    int i = 0;
1108    int lastValue = 0;
1109    int value;
1110    Iterator annIter = annotations.iterator();
1111    while(annIter.hasNext()){
1112      Annotation ann = (Annotation)annIter.next();
1113      textPane.select(ann.getStartNode().getOffset().intValue(),
1114                      ann.getEndNode().getOffset().intValue());
1115      textPane.setCharacterAttributes(style, true);
1116      value = i * 100 / size;
1117      if(value - lastValue >= 5){
1118        progressBar.setValue(value);
1119        progressBar.paintImmediately(progressBar.getBounds());
1120        lastValue = value;
1121      }
1122      i++;
1123    }
1124    //restore the state
1125    textPane.select(selStart, selEnd);
1126    SwingUtilities.invokeLater(new Runnable(){
1127      public void run(){
1128        //show the text
1129        textScroll.getViewport().setView(textPane);
1130        try{
1131          textScroll.getViewport().setViewPosition(
1132                                  textPane.modelToView(position).getLocation());
1133          textScroll.paintImmediately(textScroll.getBounds());
1134        }catch(BadLocationException ble){
1135        }
1136      }
1137    });
1138  }//protected void showHighlights()
1139
1140  /**
1141   * Updates the GUI when the user has selected an annotation e.g. by using the
1142   * right click popup. That basically means make the appropiate type of
1143   * annotations visible in case it isn't already.
1144   */
1145  protected void selectAnnotation(String set, Annotation ann) {
1146    TypeData tData = getTypeData(set, ann.getType());
1147    if(!tData.getVisible()){
1148      tData.setVisible(true);
1149      //sleep a while so the gui updater thread has time to start
1150      try{
1151        Thread.sleep(100);
1152      }catch(InterruptedException ie){}
1153      //refresh the display for the type
1154      //(the checkbox has to be shown selected)
1155      DefaultMutableTreeNode node = (DefaultMutableTreeNode)
1156                                    ((DefaultMutableTreeNode)stylesTreeRoot).
1157                                    getFirstChild();
1158      while(node != null &&
1159            !((TypeData)node.getUserObject()).getSet().equals(set))
1160        node = node.getNextSibling();
1161      if(node != null){
1162        node = (DefaultMutableTreeNode)node.getFirstChild();
1163        String type = ann.getType();
1164        while(node != null &&
1165              !((TypeData)node.getUserObject()).getType().equals(type))
1166          node = node.getNextSibling();
1167        if(node != null) stylesTreeModel.nodeChanged(node);
1168      }
1169    }
1170    int position = -1;
1171    position = data.indexOf(ann);
1172    if(position != -1){
1173      position = annotationsTable.getTableRow(position);
1174      if(position != -1){
1175        annotationsTable.clearSelection();
1176        annotationsTable.addRowSelectionInterval(position, position);
1177        annotationsTable.scrollRectToVisible(
1178              annotationsTable.getCellRect(position, 0, true));
1179      }
1180    }
1181  }//protected void selectAnnotation(String set, Annotation ann)
1182
1183
1184  /**
1185   * Creates the layout of this component acording to the set of subcomponents
1186   * (text display, annotations table, etc.) that need to be visible.
1187   */
1188  protected void layoutComponents(){
1189    SwingUtilities.invokeLater(new Runnable(){
1190      public void run(){
1191        Component leftComp = null, rightComp = null;
1192        if(isTextVisible() && isAnnotationsTableVisible()){
1193          leftSplit.setTopComponent(textScroll);
1194          leftSplit.setBottomComponent(tableScroll);
1195          leftComp = leftSplit;
1196        }else{
1197          if(isTextVisible()) leftComp = textScroll;
1198          else if(isAnnotationsTableVisible()) leftComp = tableScroll;
1199        }
1200
1201        boolean corefDisplayed = isCoreferenceVisible() &&
1202                                 isCorefOptionAvailable();
1203        if(isTypesTreeVisible() && corefDisplayed){
1204          rightSplit.setTopComponent(stylesTreeScroll);
1205          rightSplit.setBottomComponent(corefScroll);
1206          rightComp = rightSplit;
1207        }else{
1208          if(isTypesTreeVisible()) rightComp = stylesTreeScroll;
1209          else if(corefDisplayed) rightComp = corefScroll;
1210        }
1211
1212        if(DocumentEditor.this.getComponentCount() > 1)
1213          DocumentEditor.this.remove(1);
1214        if(leftComp != null && rightComp != null){
1215          //we need the main split
1216          mainSplit.setLeftComponent(leftComp);
1217          mainSplit.setRightComponent(rightComp);
1218          DocumentEditor.this.add(mainSplit);
1219        }else{
1220          if(leftComp != null) DocumentEditor.this.add(leftComp);
1221          else if(rightComp != null)DocumentEditor.this.add(rightComp);
1222        }
1223
1224        DocumentEditor.this.validate();
1225        DocumentEditor.this.repaint();
1226      }
1227    });
1228  }
1229
1230
1231  /**
1232   * Updates the coref tree from the coref data on the document's features
1233   */
1234  protected void updateCorefTree(){
1235    if(document == null || document.getFeatures() == null){
1236      //no coref data; clear the tree
1237      corefTreeRoot.removeAllChildren();
1238      corefTreeModel.nodeStructureChanged(corefTreeRoot);
1239      setCorefOptionAvailable(false);
1240      return;
1241    }
1242
1243    Map matchesMap = null;
1244    try{
1245      matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
1246    }catch(Exception e){
1247    }
1248    if(matchesMap == null){
1249      //no coref data; clear the tree
1250      Enumeration nodes = corefTreeRoot.breadthFirstEnumeration();
1251      while(nodes.hasMoreElements()){
1252        DefaultMutableTreeNode node = (DefaultMutableTreeNode)
1253                                      nodes.nextElement();
1254        if(node.getUserObject() instanceof CorefData){
1255          ((CorefData)node.getUserObject()).setVisible(false);
1256        }
1257      }
1258      corefTreeRoot.removeAllChildren();
1259      corefTreeModel.nodeStructureChanged(corefTreeRoot);
1260      setCorefOptionAvailable(false);
1261      return;
1262    }
1263
1264    //matches map is not null; check whether it's valid
1265    Iterator setsIter = matchesMap.keySet().iterator();
1266    setsLoop: while(setsIter.hasNext()){
1267      String setName = (String)setsIter.next();
1268      AnnotationSet annSet = setName == null ? document.getAnnotations() :
1269                                               document.getAnnotations(setName);
1270      Iterator entitiesIter = ((java.util.List)matchesMap.get(setName)).
1271                              iterator();
1272      //each entity is a list of annotation IDs
1273      while(entitiesIter.hasNext()){
1274        Iterator idsIter = ((java.util.List)entitiesIter.next()).iterator();
1275        while(idsIter.hasNext()){
1276          if(annSet.get((Integer)idsIter.next()) == null){
1277            //remove the data for this set
1278            setsIter.remove();
1279            Err.prln("Coreference data for the \"" +
1280                     (setName == null ? "Default" : setName) +
1281                      "\" annotation set of document \"" + document.getName() +
1282                     "\" was invalid and has been removed");
1283            continue setsLoop;
1284          }
1285        }
1286      }
1287    }
1288
1289    if(matchesMap.isEmpty()){
1290      //no more coref data
1291      corefTreeRoot.removeAllChildren();
1292      corefTreeModel.nodeStructureChanged(corefTreeRoot);
1293      setCorefOptionAvailable(false);
1294      return;
1295    }
1296
1297    String[] newSetNames = (String[])
1298                           matchesMap.keySet().toArray(new String[]{});
1299    Arrays.sort(newSetNames);
1300
1301    ArrayList oldSetNames = new ArrayList(corefTreeRoot.getChildCount());
1302    Enumeration setNodes = corefTreeRoot.children();
1303    while(setNodes.hasMoreElements()){
1304      String oldSetName = (String)
1305                           ((DefaultMutableTreeNode)setNodes.nextElement()).
1306                           getUserObject();
1307      oldSetNames.add(oldSetName.equals("Default") ? null : oldSetName);
1308    }
1309
1310
1311    // stores the new set nodes; they will be added to root after the
1312    // processing is done
1313    ArrayList newSetNodes = new ArrayList();
1314    //for each new set update the children
1315    for(int i =0; i < newSetNames.length; i++){
1316      String setName = newSetNames[i];
1317      int oldNodeIndex = oldSetNames.indexOf(setName);
1318      DefaultMutableTreeNode setNode =
1319          (oldNodeIndex != -1) ?
1320          (DefaultMutableTreeNode)
1321          corefTreeRoot.getChildAt(oldNodeIndex) :
1322          new DefaultMutableTreeNode((setName == null ? "Default" : setName),
1323                                     true);
1324      //if found it will be reused so delete it from the list
1325      if(oldNodeIndex != -1) oldSetNames.remove(oldNodeIndex);
1326
1327      // temporarily stores the new nodes
1328      ArrayList newEntityNodes = new ArrayList();
1329      //for each set the coref data is a list of lists
1330      Iterator corefDataIter = ((java.util.List)matchesMap.get(setName)).
1331                               iterator();
1332      while(corefDataIter.hasNext()){
1333        java.util.List newAnnotIDs = (java.util.List)corefDataIter.next();
1334        CorefData cData = null;
1335        DefaultMutableTreeNode entityNode = null;
1336        //try to find the old coref data
1337        Enumeration entityNodes = setNode.children();
1338        while(cData == null && entityNodes.hasMoreElements()){
1339          entityNode = (DefaultMutableTreeNode)entityNodes.nextElement();
1340          java.util.List oldAnnotIDs = ((CorefData)entityNode.getUserObject()).
1341                                     getAnnoationIDs();
1342          java.util.List intersection = new ArrayList(oldAnnotIDs);
1343          intersection.retainAll(newAnnotIDs);
1344          if(!intersection.isEmpty()){
1345            //we have some common values; assume we found it
1346            cData = (CorefData)entityNode.getUserObject();
1347            if(intersection.size() == newAnnotIDs.size()){
1348              //identical values, we just got lucky: noting to do
1349            }else{
1350              cData.setAnnotationIDs(newAnnotIDs);
1351            }
1352          }
1353        }
1354        if(cData == null){
1355          //we couldn't find a suitable node, create a new one
1356          cData = new CorefData(newAnnotIDs, false, setName == null ?
1357                                                    "Default" : setName);
1358          entityNode = new DefaultMutableTreeNode(cData, false);
1359        }
1360        newEntityNodes.add(entityNode);
1361      }//while(corefDataIter.hasNext())
1362      //we're done with this set: add all the nodes to the set node
1363      //set visible to false for all nodes that will not be kept
1364      for(Enumeration entityNodes = setNode.children();
1365          entityNodes.hasMoreElements();){
1366        Object anOldNode = entityNodes.nextElement();
1367        if(!newEntityNodes.contains(anOldNode)){
1368          ((CorefData)((DefaultMutableTreeNode)anOldNode).
1369          getUserObject()).setVisible(false);
1370        }
1371      }
1372
1373      setNode.removeAllChildren();
1374      for(Iterator nodesIter = newEntityNodes.iterator();
1375          nodesIter.hasNext();
1376          setNode.add((DefaultMutableTreeNode)nodesIter.next())){
1377      }
1378      newSetNodes.add(setNode);
1379    }//for(int i =0; i < newSetNames.length; i++)
1380    //we're done with all the sets: add the nodes to the tree root
1381    corefTreeRoot.removeAllChildren();
1382    for(Iterator nodesIter = newSetNodes.iterator();
1383        nodesIter.hasNext();){
1384      DefaultMutableTreeNode setNode = (DefaultMutableTreeNode)nodesIter.next();
1385      corefTreeRoot.add(setNode);
1386    }
1387    corefTreeModel.nodeStructureChanged(corefTreeRoot);
1388    //expand the root
1389    corefTree.expandPath(new TreePath(new Object[]{corefTreeRoot}));
1390    //expand all of root's children
1391    Enumeration children = corefTreeRoot.children();
1392    while(children.hasMoreElements()){
1393      corefTree.expandPath(new TreePath(corefTreeModel.getPathToRoot(
1394                           (DefaultMutableTreeNode)children.nextElement())));
1395    }
1396    setCorefOptionAvailable(true);
1397  }//protected void updateCorefTree()
1398
1399
1400  /**Should the editor functionality of this component be enabled*/
1401  public void setEditable(boolean newEditable) {
1402    editable = newEditable;
1403  }
1404
1405  /**Is the editor functionality enabled*/
1406  public boolean isEditable() {
1407    return editable;
1408  }
1409  public void setAnnotationsTableVisible(boolean annotationsTableVisible) {
1410    boolean  oldAnnotationsTableVisible = this.annotationsTableVisible;
1411    this.annotationsTableVisible = annotationsTableVisible;
1412    propertyChangeListeners.firePropertyChange(
1413        "annotationsTableVisible",
1414        new Boolean(oldAnnotationsTableVisible),
1415        new Boolean(annotationsTableVisible));
1416  }
1417  public boolean isAnnotationsTableVisible() {
1418    return annotationsTableVisible;
1419  }
1420  public void setCoreferenceVisible(boolean coreferenceVisible) {
1421    boolean  oldCoreferenceVisible = this.coreferenceVisible;
1422    this.coreferenceVisible = coreferenceVisible;
1423    propertyChangeListeners.firePropertyChange(
1424      "coreferenceVisible",
1425      new Boolean(oldCoreferenceVisible),
1426      new Boolean(coreferenceVisible));
1427  }
1428
1429  public boolean isCoreferenceVisible() {
1430    return coreferenceVisible;
1431  }
1432  public void setTextVisible(boolean textVisible) {
1433    boolean  oldTextVisible = this.textVisible;
1434    this.textVisible = textVisible;
1435    propertyChangeListeners.firePropertyChange("textVisible",
1436                                               new Boolean(oldTextVisible),
1437                                               new Boolean(textVisible));
1438  }
1439  public boolean isTextVisible() {
1440    return textVisible;
1441  }
1442  public void setTypesTreeVisible(boolean typesTreeVisible) {
1443    boolean  oldTypesTreeVisible = this.typesTreeVisible;
1444    this.typesTreeVisible = typesTreeVisible;
1445    propertyChangeListeners.firePropertyChange("typesTreeVisible",
1446                                               new Boolean(oldTypesTreeVisible),
1447                                               new Boolean(typesTreeVisible));
1448  }
1449  public boolean isTypesTreeVisible() {
1450    return typesTreeVisible;
1451  }
1452  public void setCorefOptionAvailable(boolean corefOptionAvailable) {
1453    boolean  oldCorefOptionAvailable = this.corefOptionAvailable;
1454    this.corefOptionAvailable = corefOptionAvailable;
1455    propertyChangeListeners.firePropertyChange(
1456      "corefOptionAvailable", new Boolean(oldCorefOptionAvailable),
1457      new Boolean(corefOptionAvailable));
1458  }
1459
1460  public boolean isCorefOptionAvailable() {
1461    return corefOptionAvailable;
1462  }
1463
1464  //inner classes
1465  /**
1466   * A custom table model used to render a table containing the annotations
1467   * from a set of annotation sets.
1468   * The columns will be: Type, Set, Start, End, Features
1469   */
1470  protected class AnnotationsTableModel extends AbstractTableModel{
1471    public AnnotationsTableModel(){
1472    }
1473
1474    public int getRowCount(){
1475      return data.size();
1476    }
1477
1478    public int getColumnCount(){
1479      return 5;
1480    }
1481
1482    public String getColumnName(int column){
1483      switch(column){
1484        case 0: return "Type";
1485        case 1: return "Set";
1486        case 2: return "Start";
1487        case 3: return "End";
1488        case 4: return "Features";
1489        default:return "?";
1490      }
1491    }
1492
1493    public Class getColumnClass(int column){
1494      switch(column){
1495        case 0: return String.class;
1496        case 1: return String.class;
1497        case 2: return Long.class;
1498        case 3: return Long.class;
1499        case 4: return String.class;
1500        default:return Object.class;
1501      }
1502    }
1503
1504    public Object getValueAt(int row, int column){
1505      Annotation ann;
1506      ann = (Annotation)data.get(row);
1507      switch(column){
1508        case -1:{//The actual annotation
1509          return ann;
1510        }
1511        case 0:{//Type
1512          return ann.getType();
1513        }
1514        case 1:{//Set
1515          Iterator rangesIter = ranges.iterator();
1516          while(rangesIter.hasNext()){
1517            Range range = (Range)rangesIter.next();
1518            if(range.start <= row && row < range.end) return range.setName;
1519          }
1520          return "?";
1521        }
1522        case 2:{//Start
1523          return ann.getStartNode().getOffset();
1524        }
1525        case 3:{//End
1526          return ann.getEndNode().getOffset();
1527        }
1528        case 4:{//Features
1529          if(ann.getFeatures() == null) return null;
1530          else return ann.getFeatures().toString();
1531        }
1532        default:{
1533        }
1534      }
1535      return null;
1536    }
1537  }//class AnnotationsTableModel extends AbstractTableModel
1538
1539/*
1540  protected class CorefListModel extends AbstractListModel{
1541    CorefListModel(){
1542      corefComboModel.addListDataListener(new ListDataListener() {
1543        public void intervalAdded(ListDataEvent e) {
1544          fireDataChanged();
1545        }
1546
1547        public void intervalRemoved(ListDataEvent e) {
1548          fireDataChanged();
1549        }
1550
1551        public void contentsChanged(ListDataEvent e) {
1552          fireDataChanged();
1553        }
1554      });
1555      coloursList = new ArrayList();
1556      visibleList = new ArrayList();
1557      highlights = new ArrayList();
1558      lastReturnedSize = 0;
1559    }
1560
1561    public int getSize(){
1562      if(document == null || document.getFeatures() == null) return 0;
1563      Map matchesMap = null;
1564      try{
1565        matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
1566      }catch(Exception e){
1567        e.printStackTrace();
1568      }
1569      if(matchesMap == null) return 0;
1570      java.util.List matchesList = (java.util.List)
1571                                   matchesMap.get(corefCombo.getSelectedItem());
1572      int size = (matchesList == null) ? 0 : matchesList.size();
1573      if(lastReturnedSize != size){
1574        lastReturnedSize = size;
1575        fireDataChanged();
1576      }
1577      return lastReturnedSize;
1578    }
1579
1580    public Object getElementAt(int index){
1581      if(document == null || document.getFeatures() == null) return null;
1582      Map matchesMap = null;
1583      try{
1584        matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
1585      }catch(Exception e){
1586        e.printStackTrace();
1587      }
1588      if(matchesMap == null) return null;
1589      java.util.List matchesList = (java.util.List)
1590                                   matchesMap.get(corefCombo.getSelectedItem());
1591      if(matchesList == null || matchesList.size() <= index) return null;
1592      java.util.List oneMatch = (java.util.List)matchesList.get(index);
1593      return oneMatch;
1594    }
1595
1596    void fireDataChanged(){
1597      fireContentsChanged(this, 0, getSize());
1598    }
1599
1600    Color getColour(int row){
1601      if(row >= coloursList.size()){
1602        for(int i = coloursList.size(); i <= row; i++){
1603          coloursList.add(i, colGenerator.getNextColor());
1604        }
1605      }
1606      return (Color)coloursList.get(row);
1607    }
1608
1609    void setColour(int row, Color color){
1610      if(row >= coloursList.size()){
1611        for(int i = coloursList.size(); i <= row; i++){
1612          coloursList.add(i, colGenerator.getNextColor());
1613        }
1614      }
1615      coloursList.set(row, color);
1616    }
1617
1618    boolean getVisible(int row){
1619      if(row >= visibleList.size()){
1620        for(int i = visibleList.size(); i <= row; i++){
1621          visibleList.add(i, new Boolean(false));
1622        }
1623      }
1624      return ((Boolean)visibleList.get(row)).booleanValue();
1625    }
1626
1627    void setVisible(int row, boolean visible){
1628      if(row >= visibleList.size()){
1629        for(int i = visibleList.size(); i <= row; i++){
1630          visibleList.add(i, new Boolean(false));
1631        }
1632      }
1633      visibleList.set(row, new Boolean(visible));
1634      java.util.List highlightsForRow = getHighlights(row);
1635      if(visible){
1636        //add new highlights and store them
1637        java.util.List ids = (java.util.List)getElementAt(row);
1638        String setName = (String)corefCombo.getSelectedItem();
1639        AnnotationSet set = setName.equals("Default") ?
1640                            document.getAnnotations() :
1641                            document.getAnnotations(setName);
1642        Iterator idIter = ids.iterator();
1643        while(idIter.hasNext()){
1644          Integer id = (Integer)idIter.next();
1645          Annotation ann = set.get(id);
1646          try{
1647            highlightsForRow.add(highlighter.addHighlight(
1648              ann.getStartNode().getOffset().intValue(),
1649              ann.getEndNode().getOffset().intValue(),
1650              new DefaultHighlighter.DefaultHighlightPainter(getColour(row))));
1651          }catch(BadLocationException ble){
1652            ble.printStackTrace();
1653          }
1654        }
1655      }else{
1656        //remove the highlights
1657        if(!highlightsForRow.isEmpty()){
1658          Iterator hlIter = highlightsForRow.iterator();
1659          while(hlIter.hasNext()){
1660            Object tag = hlIter.next();
1661            highlighter.removeHighlight(tag);
1662            hlIter.remove();
1663          }
1664        }
1665      }
1666    }//void setVisible(int row, boolean visible){
1667
1668    java.util.List getHighlights(int row){
1669      if(row >= highlights.size()){
1670        for(int i = highlights.size(); i <= row; i++){
1671          highlights.add(i, new ArrayList());
1672        }
1673      }
1674      return ((java.util.List)highlights.get(row));
1675    }
1676
1677    void setHighlights(int row, java.util.List highlightsForRow ){
1678      if(row >= highlights.size()){
1679        for(int i = highlights.size(); i <= row; i++){
1680          highlights.add(i, new ArrayList());
1681        }
1682      }
1683      highlights.set(row, highlightsForRow);
1684    }
1685
1686//    /**
1687//     * Holds the <b>visible</b> attribute for each row in the list
1688//     */
1689//    ArrayList visibleList;
1690//
1691//    /**
1692//     * Holds the <b>colour</b> attribute for each row in the list
1693//     */
1694//    ArrayList coloursList;
1695//
1696//    /**
1697//     * A list of lists; holds the currently showing highlights for each row
1698//     */
1699//    ArrayList highlights;
1700//
1701//    int lastReturnedSize;
1702//  }
1703/*
1704  class CorefListRenderer extends JCheckBox implements ListCellRenderer{
1705    public CorefListRenderer(){
1706      setOpaque(true);
1707    }
1708
1709    public Component getListCellRendererComponent(JList list,
1710                                                  Object value,
1711                                                  int index,
1712                                                  boolean isSelected,
1713                                                  boolean cellHasFocus){
1714//      if (isSelected) {
1715//        setForeground(list.getSelectionForeground());
1716//        setBackground(list.getSelectionBackground());
1717//      }else{
1718        setForeground(list.getForeground());
1719        setBackground(list.getBackground());
1720//      }
1721      setBackground(((CorefListModel)list.getModel()).getColour(index));
1722      setFont(list.getFont());
1723      if (cellHasFocus) {
1724        setBorder( UIManager.getBorder("Table.focusCellHighlightBorder") );
1725      }else{
1726        setBorder(noFocusBorder);
1727      }
1728      setText(getNameForCorefList((java.util.List) value));
1729      setSelected(((CorefListModel)list.getModel()).getVisible(index));
1730      return this;
1731    }
1732
1733    String getNameForCorefList(java.util.List list){
1734      if(list == null || list.isEmpty()) return null;
1735      Integer id = (Integer)list.get(0);
1736      String setName = (String)corefCombo.getSelectedItem();
1737      AnnotationSet set = setName.equals("Default") ?
1738                          document.getAnnotations() :
1739                          document.getAnnotations(setName);
1740      Annotation ann = set.get(id);
1741
1742      String name = null;
1743      try{
1744        name = document.getContent().
1745                        getContent(ann.getStartNode().getOffset(),
1746                                   ann.getEndNode().getOffset()).toString();
1747      }catch(InvalidOffsetException ioe){
1748      }
1749      return name;
1750    }
1751    Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
1752  }
1753
1754*/
1755  protected class CorefData{
1756    CorefData(java.util.List annotationIDs, boolean visible, String setName){
1757      this.visible = visible;
1758      this.setName = setName;
1759      this.colour = colGenerator.getNextColor();
1760      highlights = new ArrayList();
1761      this.annotationIDs = annotationIDs;
1762      this.title = getNameForCorefList(annotationIDs);
1763    }
1764
1765    /**
1766     * Finds the name for a set of co refering entities (uses the string of the
1767     * first one).
1768     * @param list a list of annotation IDs
1769     */
1770    String getNameForCorefList(java.util.List list){
1771      if(list == null || list.isEmpty()) return null;
1772      Integer id = (Integer)list.get(0);
1773      AnnotationSet set = setName.equals("Default") ?
1774                          document.getAnnotations() :
1775                          document.getAnnotations(setName);
1776      Annotation ann = set.get(id);
1777
1778      String name = null;
1779      try{
1780        name = document.getContent().
1781                        getContent(ann.getStartNode().getOffset(),
1782                                   ann.getEndNode().getOffset()).toString();
1783      }catch(InvalidOffsetException ioe){
1784      }
1785      return name;
1786    }
1787
1788    public boolean getVisible(){
1789      return visible;
1790    }
1791
1792    public void setVisible(boolean isVisible){
1793      if(this.visible == isVisible) return;
1794      this.visible = isVisible;
1795      if(visible){
1796if(!highlights.isEmpty()){
1797  Out.prln("Redundant highlights detected!");
1798}
1799        //add new highlights and store them
1800        AnnotationSet set = setName.equals("Default") ?
1801                            document.getAnnotations() :
1802                            document.getAnnotations(setName);
1803        Iterator idIter = annotationIDs.iterator();
1804        while(idIter.hasNext()){
1805          Integer id = (Integer)idIter.next();
1806          Annotation ann = set.get(id);
1807          try{
1808            highlights.add(highlighter.addHighlight(
1809              ann.getStartNode().getOffset().intValue(),
1810              ann.getEndNode().getOffset().intValue(),
1811              new DefaultHighlighter.DefaultHighlightPainter(colour)));
1812          }catch(BadLocationException ble){
1813            ble.printStackTrace();
1814          }
1815        }
1816      }else{
1817        //remove the highlights
1818        if(!highlights.isEmpty()){
1819          Iterator hlIter = highlights.iterator();
1820          while(hlIter.hasNext()){
1821            Object tag = hlIter.next();
1822            highlighter.removeHighlight(tag);
1823            hlIter.remove();
1824          }
1825        }
1826      }
1827    }
1828
1829    public String getTitle(){
1830      return title;
1831    }
1832
1833    public Color getColour(){
1834      return colour;
1835    }
1836
1837    public void setColour(Color newColour){
1838      this.colour = newColour;
1839      if(visible){
1840        //update the highlights
1841        setVisible(false);
1842        setVisible(true);
1843      }
1844    }
1845
1846    public java.util.List getAnnoationIDs(){
1847      return annotationIDs;
1848    }
1849
1850    public String toString(){
1851      return title;
1852    }
1853
1854    public void setAnnotationIDs(java.util.List newAnnIDs){
1855      this.annotationIDs =newAnnIDs;
1856      this.title = getNameForCorefList(annotationIDs);
1857      if(visible){
1858        //restore the highlights
1859        setVisible(false);
1860        setVisible(true);
1861      }
1862    }
1863
1864    private boolean visible;
1865    private String title;
1866    private String setName;
1867    private Color colour;
1868    private java.util.List highlights;
1869    private java.util.List annotationIDs;
1870  }
1871
1872/*
1873  protected class CorefComboModel extends AbstractListModel
1874                                  implements ComboBoxModel{
1875
1876    CorefComboModel(){
1877      lastReturnedSize = 0;
1878    }
1879
1880    public int getSize(){
1881      if(document == null || document.getFeatures() == null) return 0;
1882      Map matchesMap = null;
1883      try{
1884        matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
1885      }catch(Exception e){
1886        e.printStackTrace();
1887      }
1888      int size = (matchesMap == null) ? 0 : matchesMap.size();
1889      if(lastReturnedSize != size){
1890        lastReturnedSize = size;
1891        fireDataChanged();
1892      }
1893      return lastReturnedSize;
1894    }
1895
1896
1897    public Object getElementAt(int index){
1898      if(document == null || document.getFeatures() == null) return null;
1899      Map matchesMap = null;
1900      try{
1901        matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
1902      }catch(Exception e){
1903        e.printStackTrace();
1904      }
1905      if(matchesMap == null) return null;
1906      java.util.List setsList = new ArrayList(matchesMap.keySet());
1907      boolean nullPresent = setsList.remove(null);
1908      Collections.sort(setsList);
1909      if(nullPresent) setsList.add(0, null);
1910      String res = (String)setsList.get(index);
1911      return (res == null) ? "Default" : res;
1912    }
1913
1914    public void setSelectedItem(Object anItem){
1915      if(anItem == null) selectedItem = null;
1916      else selectedItem = ((String)anItem).equals("Default") ? null : anItem;
1917    }
1918
1919    public Object getSelectedItem(){
1920      return selectedItem == null ? "Default" : selectedItem;
1921    }
1922
1923    void fireDataChanged(){
1924      fireContentsChanged(this, 0, getSize());
1925    }
1926
1927    Object selectedItem = null;
1928    int lastReturnedSize;
1929  }
1930*/
1931
1932  /**
1933   * Panels used in cell/node renderers
1934   */
1935  class LazyJPanel extends JPanel{
1936    /**
1937     * Overridden for performance reasons.
1938     */
1939    public void revalidate() {}
1940
1941    /**
1942     * Overridden for performance reasons.
1943     */
1944    public void repaint(long tm, int x, int y, int width, int height) {}
1945
1946    /**
1947     * Overridden for performance reasons.
1948     */
1949    public void repaint(Rectangle r) {}
1950
1951    /**
1952     * Overridden for performance reasons.
1953     */
1954    protected void firePropertyChange(String propertyName, Object oldValue,
1955                                                            Object newValue) {}
1956
1957    /**
1958     * Overridden for performance reasons.
1959     */
1960    public void firePropertyChange(String propertyName, byte oldValue,
1961                                                              byte newValue) {}
1962
1963    /**
1964     * Overridden for performance reasons.
1965     */
1966    public void firePropertyChange(String propertyName, char oldValue,
1967                                                              char newValue) {}
1968
1969    /**
1970     * Overridden for performance reasons.
1971     */
1972    public void firePropertyChange(String propertyName, short oldValue,
1973                                                            short newValue) {}
1974
1975    /**
1976     * Overridden for performance reasons.
1977     */
1978    public void firePropertyChange(String propertyName, int oldValue,
1979                                                              int newValue) {}
1980
1981    /**
1982     * Overridden for performance reasons.
1983     */
1984    public void firePropertyChange(String propertyName, long oldValue,
1985                                                              long newValue) {}
1986
1987    /**
1988     * Overridden for performance reasons.
1989     */
1990    public void firePropertyChange(String propertyName, float oldValue,
1991                                                              float newValue) {}
1992
1993    /**
1994     * Overridden for performance reasons.
1995     */
1996    public void firePropertyChange(String propertyName, double oldValue,
1997                                                            double newValue) {}
1998
1999    /**
2000     * Overridden for performance reasons.
2001     */
2002    public void firePropertyChange(String propertyName, boolean oldValue,
2003                                                            boolean newValue) {}
2004  }
2005
2006  /**
2007   * A tree node renderer used by the coref tree
2008   */
2009  class CorefNodeRenderer implements TreeCellRenderer{
2010
2011    CorefNodeRenderer(){
2012      label = new JLabel();
2013      label.setOpaque(true);
2014
2015      checkBox = new JCheckBox();
2016      checkBox.setBorderPaintedFlat(true);
2017
2018      panel = new LazyJPanel();
2019      panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
2020      panel.setOpaque(false);
2021
2022      hBox = new LazyJPanel();
2023      hBox.setLayout(new BoxLayout(hBox, BoxLayout.X_AXIS));
2024      hBox.setOpaque(false);
2025
2026      panel.add(Box.createVerticalStrut(2));
2027      panel.add(hBox);
2028      panel.add(Box.createVerticalStrut(2));
2029
2030      leftSpacer = Box.createHorizontalStrut(3);
2031      rightSpacer = Box.createHorizontalStrut(3);
2032
2033      selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
2034      normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
2035    }
2036
2037    public Component getTreeCellRendererComponent(JTree tree,
2038                                              Object value,
2039                                              boolean selected,
2040                                              boolean expanded,
2041                                              boolean leaf,
2042                                              int row,
2043                                              boolean hasFocus){
2044
2045      hBox.removeAll();
2046      hBox.add(leftSpacer);
2047
2048      if(value instanceof DefaultMutableTreeNode){
2049        value = ((DefaultMutableTreeNode)value).getUserObject();
2050      }
2051      if(value instanceof CorefData){
2052        CorefData cData = (CorefData)value;
2053        checkBox.setSelected(cData.getVisible());
2054        checkBox.setBackground(tree.getBackground());
2055
2056        label.setBackground(cData.getColour());
2057        label.setForeground(tree.getForeground());
2058        label.setText(cData.getTitle());
2059        label.setFont(tree.getFont());
2060        hBox.add(checkBox);
2061        hBox.add(label);
2062        hBox.add(rightSpacer);
2063      }else{
2064        label.setText(value == null ? "" : value.toString());
2065        label.setForeground(tree.getForeground());
2066        label.setBackground(tree.getBackground());
2067        label.setFont(tree.getFont());
2068        hBox.add(label);
2069      }
2070      if(selected) panel.setBorder(selectedBorder);
2071      else panel.setBorder(normalBorder);
2072      return panel;
2073    }
2074
2075    JLabel label;
2076    JCheckBox checkBox;
2077    JPanel panel;
2078    JPanel hBox;
2079    Border selectedBorder;
2080    Border normalBorder;
2081    Component leftSpacer, rightSpacer;
2082  }
2083
2084  /**
2085   * A tree node renderer used byt the coref tree
2086   */
2087  class CorefNodeRenderer1 implements TreeCellRenderer{
2088
2089    CorefNodeRenderer1(){
2090      label = new JLabel();
2091      label.setOpaque(true);
2092
2093      toggleButton = new JToggleButton();
2094      toggleButton.setMargin(new Insets(0,3,0,3));
2095
2096      panel = new LazyJPanel();
2097      panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
2098      panel.setOpaque(false);
2099      topSpacer = Box.createVerticalStrut(2);
2100      bottomSpacer = Box.createVerticalStrut(2);
2101
2102      selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
2103      normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
2104
2105    }
2106
2107    public Component getTreeCellRendererComponent(JTree tree,
2108                                              Object value,
2109                                              boolean selected,
2110                                              boolean expanded,
2111                                              boolean leaf,
2112                                              int row,
2113                                              boolean hasFocus){
2114
2115      panel.removeAll();
2116      panel.add(topSpacer);
2117
2118      if(value instanceof DefaultMutableTreeNode){
2119        value = ((DefaultMutableTreeNode)value).getUserObject();
2120      }
2121      if(value instanceof CorefData){
2122        CorefData cData = (CorefData)value;
2123        toggleButton.setSelected(cData.getVisible());
2124        toggleButton.setBackground(cData.getColour());
2125        toggleButton.setForeground(tree.getForeground());
2126        toggleButton.setText(cData.getTitle());
2127        toggleButton.setFont(tree.getFont());
2128        panel.add(toggleButton);
2129      }else{
2130        label.setText(value.toString());
2131        label.setForeground(tree.getForeground());
2132        label.setBackground(tree.getBackground());
2133        label.setFont(tree.getFont());
2134        panel.add(label);
2135      }
2136      panel.add(bottomSpacer);
2137      if(selected) panel.setBorder(selectedBorder);
2138      else panel.setBorder(normalBorder);
2139      return panel;
2140    }
2141
2142    JLabel label;
2143    JToggleButton toggleButton;
2144    JPanel panel;
2145    Border selectedBorder;
2146    Border normalBorder;
2147    Component topSpacer, bottomSpacer;
2148  }
2149
2150
2151  /**
2152   * Displays an entry in the right hand side tree.
2153   * <strong>Implementation Note:</strong>
2154   * This class overrides
2155   * <code>revalidate</code>,
2156   * <code>repaint</code>,
2157   * and
2158   * <code>firePropertyChange</code>
2159   * solely to improve performance.
2160   * If not overridden, these frequently called methods would execute code paths
2161   * that are unnecessary for a tree cell renderer.
2162   */
2163  class NodeRenderer extends LazyJPanel implements TreeCellRenderer{
2164
2165    public NodeRenderer(){
2166      visibleChk = new JCheckBox("",false);
2167      visibleChk.setOpaque(false);
2168      visibleChk.setBorderPaintedFlat(true);
2169
2170      label = new JLabel();
2171      label.setOpaque(true);
2172      fontAttrs = new HashMap();
2173      selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
2174      normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
2175      setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
2176      setOpaque(false);
2177      spacer = Box.createHorizontalStrut(3);
2178    }
2179
2180    public Component getTreeCellRendererComponent(JTree tree,
2181                                              Object value,
2182                                              boolean selected,
2183                                              boolean expanded,
2184                                              boolean leaf,
2185                                              int row,
2186                                              boolean hasFocus){
2187      removeAll();
2188      add(spacer);
2189
2190      int width = spacer.getWidth();
2191
2192
2193      TypeData nData = (TypeData)
2194                            ((DefaultMutableTreeNode)value).getUserObject();
2195
2196      if(nData != null){
2197        label.setText(nData.getTitle());
2198        setLabelAttributes(nData.getAttributes());
2199
2200        if(nData.getType() != null) {
2201          visibleChk.setSelected(nData.getVisible());
2202          add(visibleChk);
2203          width += visibleChk.getMinimumSize().width;
2204        }
2205      }else{
2206        label.setText(((value == null || value.toString() == null) ?
2207                              "" : value.toString()));
2208      }
2209      add(label);
2210
2211      if(selected) setBorder(selectedBorder);
2212      else setBorder(normalBorder);
2213      return this;
2214    }//public Component getTreeCellRendererComponent
2215
2216    protected void setLabelAttributes(AttributeSet style){
2217      label.setForeground(StyleConstants.getForeground(style));
2218      label.setBackground(StyleConstants.getBackground(style));
2219      fontAttrs.clear();
2220      fontAttrs.put(TextAttribute.FAMILY, StyleConstants.getFontFamily(style));
2221      fontAttrs.put(TextAttribute.SIZE, new Float(StyleConstants.getFontSize(style)));
2222      if(StyleConstants.isBold(style))
2223        fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
2224      else fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR);
2225      if(StyleConstants.isItalic(style))
2226        fontAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
2227      else fontAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR);
2228      if(StyleConstants.isUnderline(style))
2229        fontAttrs.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
2230      else fontAttrs.remove(TextAttribute.UNDERLINE);
2231      if(StyleConstants.isStrikeThrough(style))
2232        fontAttrs.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
2233      else fontAttrs.remove(TextAttribute.STRIKETHROUGH);
2234      if(StyleConstants.isSuperscript(style))
2235        fontAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER);
2236      else if(StyleConstants.isSubscript(style))
2237        fontAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB);
2238      else fontAttrs.remove(TextAttribute.SUPERSCRIPT);
2239
2240      label.setFont(new Font(fontAttrs));
2241    }
2242
2243    Border selectedBorder;
2244    Border normalBorder;
2245    JCheckBox visibleChk;
2246    JLabel label;
2247    Map fontAttrs;
2248    Component spacer;
2249  }//class NodeRenderer extends JPanel implements TreeCellRenderer
2250  /**
2251   * Displays an entry in the right hand side tree.
2252   * <strong>Implementation Note:</strong>
2253   * This class overrides
2254   * <code>revalidate</code>,
2255   * <code>repaint</code>,
2256   * and
2257   * <code>firePropertyChange</code>
2258   * solely to improve performance.
2259   * If not overridden, these frequently called methods would execute code paths
2260   * that are unnecessary for a tree cell renderer.
2261   */
2262  class NodeRenderer1 extends LazyJPanel implements TreeCellRenderer{
2263
2264    public NodeRenderer1(){
2265      visibleChk = new JCheckBox("",false);
2266      visibleChk.setOpaque(false);
2267      visibleChk.setBorderPaintedFlat(true);
2268      textComponent = new JTextPane();
2269      selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
2270      normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
2271      setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
2272      setOpaque(false);
2273      spacer = Box.createHorizontalStrut(3);
2274    }
2275
2276    public Component getTreeCellRendererComponent(JTree tree,
2277                                              Object value,
2278                                              boolean selected,
2279                                              boolean expanded,
2280                                              boolean leaf,
2281                                              int row,
2282                                              boolean hasFocus){
2283      removeAll();
2284      add(spacer);
2285
2286      int width = spacer.getWidth();
2287
2288      //the text pane needs to be sized for modelToView() to work
2289      textComponent.setSize(1000, 1000);
2290
2291      TypeData nData = (TypeData)
2292                            ((DefaultMutableTreeNode)value).getUserObject();
2293//      javax.swing.text.Document doc = textComponent.getDocument();
2294
2295      if(nData != null){
2296        textComponent.setText(nData.getTitle());
2297        textComponent.selectAll();
2298        textComponent.setCharacterAttributes(nData.getAttributes(), false);
2299        textComponent.select(0, 0);
2300//        try{
2301//          doc.remove(0, doc.getLength());
2302//          doc.insertString(0, nData.getTitle(),
2303//                           nData.getAttributes());
2304//        }catch(BadLocationException ble){
2305//          ble.printStackTrace();
2306//        }
2307
2308        if(nData.getType() != null) {
2309          visibleChk.setSelected(nData.getVisible());
2310          add(visibleChk);
2311          width += visibleChk.getMinimumSize().width;
2312        }
2313      }else{
2314        textComponent.setText(((value == null || value.toString() == null) ?
2315                              "" : value.toString()));
2316//        try{
2317//          doc.remove(0, doc.getLength());
2318//          doc.insertString(0, value.toString(),
2319//                           textComponent.getStyle("default"));
2320//        }catch(BadLocationException ble){
2321//          ble.printStackTrace();
2322//        }
2323      }
2324      setTextComponentSize(textComponent);
2325      add(textComponent);
2326      width += textComponent.getPreferredSize().width;
2327      if(selected) setBorder(selectedBorder);
2328      else setBorder(normalBorder);
2329      width += getInsets().left + getInsets().right;
2330      setPreferredSize(null);
2331      setPreferredSize(new Dimension(width, super.getPreferredSize().height));
2332      return this;
2333    }//public Component getTreeCellRendererComponent
2334
2335   protected void setTextComponentSize(JTextComponent comp){
2336      try{
2337        if(comp.getDocument() == null || comp.getDocument().getLength() <= 0){
2338          return;
2339        }
2340        int width = 0;
2341        Rectangle rect = comp.modelToView(0);
2342        int height = rect.height;
2343        int length = comp.getDocument().getLength();
2344        if(length > 0){
2345          Rectangle rect2 = comp.modelToView(length );
2346          if(rect2 != null){
2347            if(rect.x < rect2.x){
2348              //left to right
2349              width = rect2.x + rect2.width - rect.x;
2350            }else{
2351              //RtL
2352              width = rect.x +rect.width - rect2.x;
2353            }
2354            height = Math.max(height, rect2.height);
2355          }
2356        }
2357        Insets insets = comp.getInsets();
2358        Dimension dim = new Dimension(width + insets.left + insets.right + 5,
2359                                      height + insets.top + insets.bottom);
2360        comp.setPreferredSize(dim);
2361      }catch(BadLocationException ble){
2362        //this will work the next time around so it's safe to ignore it now
2363      }
2364    }
2365    Border selectedBorder;
2366    Border normalBorder;
2367    JCheckBox visibleChk;
2368    JTextPane textComponent;
2369    Component spacer;
2370  }//class NodeRenderer extends JPanel implements TreeCellRenderer
2371
2372  /**
2373   * Displays an entry in the right hand side tree.
2374   * <strong><a name="override">Implementation Note:</a></strong>
2375   * This class overrides
2376   * <code>revalidate</code>,
2377   * <code>repaint</code>,
2378   * and
2379   * <code>firePropertyChange</code>
2380   * solely to improve performance.
2381   * If not overridden, these frequently called methods would execute code paths
2382   * that are unnecessary for a tree cell renderer.
2383   */
2384/*
2385  class NodeRenderer1 extends JPanel implements TreeCellRenderer{
2386
2387    public NodeRenderer1(){
2388      visibleChk = new JCheckBox("",false);
2389      visibleChk.setOpaque(false);
2390      typeComponent = new JTextPane();
2391      setComponent = new JTextPane();
2392      selectedBorder = BorderFactory.createLineBorder(Color.blue);
2393      normalBorder = BorderFactory.createEmptyBorder(1,1,1,1);
2394
2395      setPanel = new LazyJPanel();
2396      setPanel.setOpaque(false);
2397      setPanel.setLayout(new BoxLayout(setPanel, BoxLayout.X_AXIS));
2398      setPanel.add(setComponent);
2399      typePanel = new LazyJPanel();
2400      typePanel.setOpaque(false);
2401      typePanel.setLayout(new BoxLayout(typePanel, BoxLayout.X_AXIS));
2402      typePanel.add(visibleChk);
2403      typePanel.add(typeComponent);
2404    }
2405
2406    public Component getTreeCellRendererComponent(JTree tree,
2407                                              Object value,
2408                                              boolean selected,
2409                                              boolean expanded,
2410                                              boolean leaf,
2411                                              int row,
2412                                              boolean hasFocus){
2413      JComponent renderer = null;
2414      TypeData nData = (TypeData)
2415                            ((DefaultMutableTreeNode)value).getUserObject();
2416      if(nData != null){
2417        if(nData.getType() != null) {
2418          visibleChk.setSelected(nData.getVisible());
2419          typeComponent.setSize(1000, 1000);
2420          javax.swing.text.Document doc = typeComponent.getDocument();
2421          try{
2422            doc.remove(0, doc.getLength());
2423            doc.insertString(0, nData.getTitle(), nData.getAttributes());
2424          }catch(BadLocationException ble){
2425            ble.printStackTrace();
2426          }
2427          setTextComponentSize(typeComponent);
2428//          typePanel.removeAll();
2429//          typePanel.add(visibleChk);
2430//          typePanel.add(typeComponent);
2431          renderer = typePanel;
2432        }else{
2433          setComponent.setSize(1000, 1000);
2434          javax.swing.text.Document doc = setComponent.getDocument();
2435          try{
2436            doc.remove(0, doc.getLength());
2437            doc.insertString(0, nData.getTitle(), nData.getAttributes());
2438          }catch(BadLocationException ble){
2439            ble.printStackTrace();
2440          }
2441          setTextComponentSize(setComponent);
2442//          setPanel.removeAll();
2443//          setPanel.add(setComponent);
2444          renderer = setPanel;
2445        }
2446      }else{
2447        setComponent.setSize(1000, 1000);
2448        javax.swing.text.Document doc = setComponent.getDocument();
2449        try{
2450          doc.remove(0, doc.getLength());
2451          doc.insertString(0, value.toString(), setComponent.getStyle("default"));
2452        }catch(BadLocationException ble){
2453          ble.printStackTrace();
2454        }
2455        setTextComponentSize(setComponent);
2456//        setPanel.removeAll();
2457//        setPanel.add(setComponent);
2458        renderer = setPanel;
2459      }
2460      if(selected) renderer.setBorder(selectedBorder);
2461      else renderer.setBorder(normalBorder);
2462      return renderer;
2463    }//public Component getTreeCellRendererComponent
2464
2465    protected void setTextComponentSize(JTextComponent comp){
2466      try{
2467        Rectangle rect = comp.modelToView(0);
2468        int length = comp.getDocument().getLength();
2469        if(length > 0){
2470          Rectangle rect2 = comp.modelToView(length - 1);
2471          if(rect2 != null){
2472Out.pr("Rect2.x " + rect2.x);
2473            //this mutates rect
2474            rect = SwingUtilities.computeUnion(rect2.x, rect2.y, rect2.width,
2475                                        rect2.height, rect);
2476Out.prln("Rect.width " + rect.width);
2477          }else{
2478Out.prln("NULL size");
2479          }
2480        }
2481        Insets insets = comp.getInsets();
2482        Dimension dim = new Dimension(rect.width + insets.left + insets.right,
2483                                      rect.height + insets.top + insets.bottom);
2484        comp.setPreferredSize(dim);
2485      }catch(BadLocationException ble){
2486        ble.printStackTrace();
2487      }
2488    }
2489
2490    Border selectedBorder;
2491    Border normalBorder;
2492    JCheckBox visibleChk;
2493    JTextPane setComponent;
2494    JTextPane typeComponent;
2495    JPanel setPanel;
2496    JPanel typePanel;
2497  }//class NodeRenderer extends JPanel implements TreeCellRenderer
2498*/
2499  /**
2500   * Holds the GUI metadata for a given annotation type. An annotation type is
2501   * uniquely identified by the name of its AnnotationSet and the name of the
2502   * type.
2503   * For the default annotation set of a document (which has no name) the
2504   * &quot;&lt;Default&gt;&quot; value is used.
2505   * The GUI metadata contains, amongst other things, the style used for
2506   * highlighting the annotations of this type.
2507   * These styles are cascading styles (there is a relation of inheritance
2508   * between them) so the annotation type style inherits the characteristics
2509   * from the style associated with the annotation set it belongs to.
2510   *
2511   * For eficiency reasons there are some intermediary styles between a parent
2512   * and a child style that used for changing the display in one operation.
2513   */
2514  public class TypeData {
2515
2516    public TypeData(String set, String type, boolean visible){
2517      this.set = set;
2518      this.type = type;
2519      this.visible = visible;
2520      Map setMap = (Map)typeDataMap.get(set);
2521      if(setMap == null){
2522        setMap = new HashMap();
2523        typeDataMap.put(set, setMap);
2524      }
2525      if(type == null) {
2526        //this node represents a Set
2527        style = textPane.addStyle(set, textPane.getStyle("default"));
2528      } else {
2529        style = textPane.addStyle(set + "." + type, textPane.getStyle(set));
2530        StyleConstants.setBackground(style,
2531                                        colGenerator.getNextColor().brighter());
2532        //add an intermediary style that will be used for the actual display
2533        textPane.addStyle("_" + set + "." + type, style);
2534        //add the style that will be used for the actual display
2535        textPane.addStyle("_" + set + "." + type + "_",
2536                          textPane.getStyle("_" + set + "." + type));
2537        setMap.put(type, this);
2538      }
2539    }
2540
2541    public String getSet() { return set;}
2542
2543    public void setSet(String set) {this.set = set;}
2544
2545    public String getType() {return type;}
2546
2547    public String getTitle() {return (type == null) ? set + " annotations" :
2548                                                      type;}
2549    public boolean getVisible() {return visible;}
2550
2551    public void setVisible(boolean isVisible) {
2552      if(this.visible == isVisible) return;
2553      this.visible = isVisible;
2554      //this is most likely called from the SWING thread so we want to get
2555      //out of here as quickly as possible. We'll start a new thread that will
2556      //do all that needs doing
2557      Runnable runnable = new Runnable() {
2558        public void run() {
2559          if(visible) {
2560            //make the corresponding range visible
2561            //update the annotations table
2562            synchronized(data) {
2563              range = new Range(set, type, data.size(),
2564                                data.size() + annotations.size());
2565              ranges.add(range);
2566              data.addAll(annotations);
2567              SwingUtilities.invokeLater(new Runnable() {
2568                public void run() {
2569                  annotationsTableModel.fireTableDataChanged();
2570                }
2571              });
2572            }
2573
2574            //update the text display
2575            Style actualStyle = textPane.getStyle("_" + set + "." + type);
2576            actualStyle.setResolveParent(style);
2577            showHighlights(annotations, textPane.getStyle("_" + set + "."
2578                                                          + type + "_"));
2579          } else {
2580            //hide the corresponding range
2581            //update the annotations table
2582            Collections.sort(ranges);
2583            Iterator rangesIter = ranges.iterator();
2584            while(rangesIter.hasNext()) {
2585              //find my range
2586              Range aRange = (Range)rangesIter.next();
2587              if(aRange == range){
2588                rangesIter.remove();
2589                int size = range.end - range.start;
2590                //remove the elements from Data
2591                data.subList(range.start, range.end).clear();
2592                //shift back all the remaining ranges
2593                while(rangesIter.hasNext()) {
2594                  aRange = (Range)rangesIter.next();
2595                  aRange.start -= size;
2596                  aRange.end -= size;
2597                }
2598              }
2599            }
2600            range = null;
2601            SwingUtilities.invokeLater(new Runnable() {
2602              public void run() {
2603                annotationsTableModel.fireTableDataChanged();
2604              }
2605            });
2606            //update the text display
2607            Style actualStyle = textPane.getStyle("_" + set + "." + type);
2608            actualStyle.setResolveParent(textPane.getStyle("default"));
2609          }//if(visible)
2610        }//public void run()
2611      };//Runnable runnable = new Runnable()
2612      Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
2613                                   runnable,
2614                                   "AnnotationEditor4");
2615      thread.setPriority(Thread.MIN_PRIORITY);
2616      thread.start();
2617    }//public void setVisible(boolean isVisible)
2618
2619    public AttributeSet getAttributes() { return style;}
2620
2621    public void setAttributes(AttributeSet newAttributes) {
2622      style.removeAttributes(style.copyAttributes());
2623      style.addAttributes(newAttributes);
2624    }
2625
2626
2627    public void setAnnotations(Set as) {
2628      this.annotations = as;
2629    }
2630
2631    public Set getAnnotations() {
2632      return annotations;
2633    }
2634
2635    public String toString() {return getTitle();}
2636
2637    private String set;
2638    private String type;
2639    private boolean visible;
2640    private Style style;
2641    private Set annotations = null;
2642    private Range range = null;
2643  }//class TypeData
2644
2645
2646  /**
2647   * Describes a range in the {@link #data} structure. A range is a bunch of
2648   * annotations of the same type belonging to the same annotation set that
2649   * are contiguous in the {@link #data} structure.
2650   */
2651  class Range implements Comparable {
2652    public Range(String setName, String type, int start, int end) {
2653      this.setName = setName;
2654      this.type = type;
2655      this.start = start;
2656      this.end = end;
2657    }
2658
2659    public String toString() {
2660      return setName +  ", " + type + " (" + start + ", " + end + ")";
2661    }
2662
2663    public int compareTo(Object other) {
2664      if(other instanceof Range) return start - ((Range)other).start;
2665      else throw new ClassCastException("Can't compare a " +
2666                                         other.getClass() + " to a " +
2667                                         getClass() + "!");
2668    }
2669
2670    String setName;
2671    String type;
2672    int start;
2673    int end;
2674  }//class Range
2675
2676
2677  /**
2678   * All the events from the document or its annotation sets are handled by
2679   * this inner class.
2680   */
2681  class EventsHandler implements gate.event.DocumentListener,
2682                                 AnnotationSetListener{
2683
2684    public void annotationSetAdded(gate.event.DocumentEvent e) {
2685      String setName = e.getAnnotationSetName();
2686      AnnotationSet as = (setName == null ? document.getAnnotations() :
2687                             document.getAnnotations(setName));
2688
2689      as.addAnnotationSetListener(this);
2690      if(setName == null) setName = "Default";
2691      TypeData setData = new TypeData(setName, null, false);
2692      setData.setAnnotations(as);
2693
2694      SwingUtilities.invokeLater(new NodeAdder(setData));
2695
2696      ArrayList typesLst = new ArrayList(as.getAllTypes());
2697      Collections.sort(typesLst);
2698
2699      Iterator typesIter = typesLst.iterator();
2700      while(typesIter.hasNext()){
2701        String type = (String)typesIter.next();
2702        TypeData typeData = new TypeData(setName, type, false);
2703        AnnotationSet sameType = as.get(type);
2704        typeData.setAnnotations(sameType);
2705
2706        SwingUtilities.invokeLater(new NodeAdder(typeData));
2707      }
2708    }
2709
2710    public void annotationSetRemoved(gate.event.DocumentEvent e) {
2711      //we access the GUI a lot here so we'll do everything from the
2712      //Swing thread
2713      SwingUtilities.invokeLater(
2714                     new SetRemovedOperation(e.getAnnotationSetName()));
2715    }//public void annotationSetRemoved(gate.event.DocumentEvent e)
2716
2717    public void annotationAdded(AnnotationSetEvent e) {
2718      AnnotationSet set = (AnnotationSet)e.getSource();
2719      String setName = set.getName();
2720      if(setName == null) setName = "Default";
2721      Annotation ann = e.getAnnotation();
2722      String type = ann.getType();
2723      TypeData tData = getTypeData(setName, type);
2724
2725      boolean tableChanged = false;
2726      if(tData != null){
2727//                tData.annotations.add(ann);
2728        if(tData.getVisible()){
2729          //1) update the table
2730          data.add(tData.range.end, ann);
2731          tData.range.end++;
2732          Iterator rangesIter = ranges.
2733                                subList(
2734                                    ranges.indexOf(tData.range) + 1,
2735                                        ranges.size()).
2736                                iterator();
2737          while(rangesIter.hasNext()){
2738            Range aRange = (Range) rangesIter.next();
2739            aRange.start++;
2740            aRange.end++;
2741          }//while(rangesIter.hasNext())
2742          tableChanged = true;
2743
2744          //2) update the text
2745          SwingUtilities.invokeLater(
2746                         new HihglightsShower(ann,
2747                                              textPane.getStyle(
2748                                                "_" + setName + "." +
2749                                                type + "_")));
2750        }//if(tData.getVisible())
2751      } else {
2752        //new type
2753        Map setMap = (Map)typeDataMap.get(setName);
2754        if(setMap == null){
2755          setMap = new HashMap();
2756          typeDataMap.put(setName, setMap);
2757        }
2758        tData = new TypeData(setName, type, false);
2759        tData.setAnnotations(set.get(type));
2760        setMap.put(type, tData);
2761
2762        SwingUtilities.invokeLater(new NodeAdder(tData));
2763
2764
2765      }//new type
2766
2767      if(tableChanged){
2768        SwingUtilities.invokeLater(new Runnable() {
2769          public void run(){
2770            if(annotationsTableModel != null){
2771              annotationsTableModel.fireTableDataChanged();
2772            }
2773          }
2774        });
2775      }//if(tableChanged)
2776    }//public void annotationAdded(AnnotationSetEvent e)
2777
2778    public void annotationRemoved(AnnotationSetEvent e){
2779      AnnotationSet set = (AnnotationSet)e.getSource();
2780      String setName = set.getName();
2781      if(setName == null) setName = "Default";
2782      Annotation ann = e.getAnnotation();
2783      String type = ann.getType();
2784      TypeData tData = getTypeData(setName, type);
2785      boolean tableChanged = false;
2786
2787      if(tData != null){
2788//                tData.annotations.remove(ann);
2789        if(tData.getVisible()){
2790          //1) update the annotations table
2791          data.remove(ann);
2792          //shorten the range conatining the annotation
2793          tData.range.end--;
2794          //shift all the remaining ranges
2795          Iterator rangesIter = ranges.
2796                              subList(ranges.indexOf(tData.range) + 1,
2797                              ranges.size()).
2798                                iterator();
2799          while(rangesIter.hasNext()){
2800            Range aRange = (Range) rangesIter.next();
2801            aRange.start--;
2802            aRange.end--;
2803          }//while(rangesIter.hasNext())
2804          tableChanged = true;
2805
2806          //update the text -> hide the highlight
2807          SwingUtilities.invokeLater(new HighlightsRemover(ann));
2808        }//if(tData.getVisible())
2809        //if this was the last annotation of this type remove the type node
2810        if((tData.annotations.size() == 1 &&
2811           tData.annotations.iterator().next() == ann) ||
2812           tData.annotations.size() == 0){
2813          //no more annotations of this type -> delete the node
2814          //first find the set
2815          DefaultMutableTreeNode node = (DefaultMutableTreeNode)
2816            ((DefaultMutableTreeNode)stylesTreeRoot).getFirstChild();
2817          while(node != null &&
2818            !((TypeData)node.getUserObject()).getSet().equals(setName))
2819            node = node.getNextSibling();
2820          if(node != null && node.getChildCount() > 0){
2821            node = (DefaultMutableTreeNode)node.getFirstChild();
2822            while(node != null &&
2823              !((TypeData)node.getUserObject()).getType().equals(type))
2824              node = node.getNextSibling();
2825            if(node != null){
2826              SwingUtilities.invokeLater(new NodeRemover(node));
2827            }
2828          }
2829          //remove the data for this type
2830          Map setMap = (Map)typeDataMap.get(setName);
2831          setMap.remove(tData.getType());
2832        }//if(tData.getAnnotations().isEmpty())
2833      }//if(tData != null)
2834
2835      if(tableChanged){
2836        SwingUtilities.invokeLater(new Runnable() {
2837          public void run(){
2838            if(annotationsTableModel != null){
2839              annotationsTableModel.fireTableDataChanged();
2840            }
2841          }
2842        });
2843      }//if(tableChanged)
2844    }//public void annotationRemoved(AnnotationSetEvent e)
2845
2846    /**
2847     * Helper class that removes one highlight corresponding to an annotation.
2848     */
2849    class HighlightsRemover implements Runnable{
2850      HighlightsRemover(Annotation ann){
2851        this.ann = ann;
2852      }
2853      public void run(){
2854        int selStart = textPane.getSelectionStart();
2855        int selEnd = textPane.getSelectionEnd();
2856        textPane.select(ann.getStartNode().getOffset().intValue(),
2857                        ann.getEndNode().getOffset().intValue());
2858        textPane.setCharacterAttributes(
2859                  textPane.getStyle("default"), true);
2860        textPane.select(selStart, selEnd);
2861      }
2862      Annotation ann;
2863    }//class HihglightsRemover implements Runnable
2864
2865    /**
2866     * Helper class that highlights a given annotation with the specified style.
2867     */
2868    class HihglightsShower implements Runnable{
2869      HihglightsShower(Annotation ann, Style style){
2870        this.ann = ann;
2871        this.style = style;
2872      }
2873      public void run(){
2874        textPane.select(ann.getStartNode().getOffset().intValue(),
2875                        ann.getEndNode().getOffset().intValue());
2876        textPane.setCharacterAttributes(style, true);
2877      }
2878      Annotation ann;
2879      Style style;
2880    }//class HihglightsRemover implements Runnable
2881
2882    /**
2883     * Helper class that removes one node from the types tree.
2884     */
2885    class NodeRemover implements Runnable{
2886      NodeRemover(DefaultMutableTreeNode node){
2887        this.node = node;
2888      }
2889      public void run(){
2890        stylesTreeModel.removeNodeFromParent(node);
2891      }
2892      DefaultMutableTreeNode node;
2893    }//class NodeRemover implements Runnable
2894
2895    /**
2896     * Helper class that adds a specified tree node
2897     */
2898    class NodeAdder implements Runnable{
2899      NodeAdder(TypeData tData){
2900        this.tData = tData;
2901      }
2902      public void run(){
2903        //create the new node
2904        DefaultMutableTreeNode newNode =
2905                  new DefaultMutableTreeNode(tData, tData.getType() == null);
2906
2907        //find its parent
2908        DefaultMutableTreeNode node = null;
2909        if(tData.getType() == null){
2910          //set node
2911          node = (DefaultMutableTreeNode)stylesTreeRoot;
2912//System.out.println("Set node " + tData.getSet());
2913        }else{
2914//System.out.println("Type node " + tData.getSet() + ":" + tData.getType());
2915          node = (DefaultMutableTreeNode)
2916            ((DefaultMutableTreeNode)stylesTreeRoot).getFirstChild();
2917          while(node != null &&
2918            !((TypeData)node.getUserObject()).getSet().equals(tData.getSet()))
2919            node = node.getNextSibling();
2920        }
2921
2922        //we have to add typeNode to node
2923        //find the right place
2924        int i = 0;
2925        if(tData.getType() == null){
2926          while (i < node.getChildCount() &&
2927                ((TypeData)
2928                  ((DefaultMutableTreeNode)node.getChildAt(i)).
2929                  getUserObject()
2930                ).getSet().compareTo(tData.getSet())<0) i++;
2931        }else{
2932          while (i < node.getChildCount() &&
2933                ((TypeData)
2934                  ((DefaultMutableTreeNode)node.getChildAt(i)).
2935                  getUserObject()
2936                ).getType().compareTo(tData.getType())<0) i++;
2937        }
2938
2939        //insert it!
2940        stylesTreeModel.insertNodeInto(newNode, node, i);
2941
2942        if(tData.getType() == null){
2943          //set node, expand it!
2944          stylesTree.expandPath(new TreePath(new Object[]{stylesTreeRoot,
2945                                                          newNode}));
2946        }
2947      }
2948
2949      TypeData tData;
2950    }//class NodeAdder implements Runnable
2951
2952    /**
2953     * Helper class that handles the removal of a named annotation set.
2954     * This runnable should only be called from the Swing thread
2955     */
2956    class SetRemovedOperation implements Runnable{
2957      SetRemovedOperation(String setName){
2958        this.setName = setName;
2959      }
2960
2961      public void run(){
2962        //find the set node
2963        Enumeration setNodesEnum = stylesTreeRoot.children();
2964        DefaultMutableTreeNode setNode = null;
2965        boolean done = false;
2966        while(!done && setNodesEnum.hasMoreElements()){
2967          setNode = (DefaultMutableTreeNode)setNodesEnum.nextElement();
2968          done = ((TypeData)setNode.getUserObject()).getSet().equals(setName);
2969        }
2970
2971        if(!((TypeData)setNode.getUserObject()).getSet().equals(setName)){
2972          throw new GateRuntimeException(
2973                "Could not find the tree node for the " + setName +
2974                " annotation set!");
2975        }
2976
2977        boolean tableChanged = false;
2978        Enumeration typeNodesEnum = setNode.children();
2979        while(typeNodesEnum.hasMoreElements()){
2980          DefaultMutableTreeNode typeNode =
2981            (DefaultMutableTreeNode)typeNodesEnum.nextElement();
2982          TypeData tData = (TypeData)typeNode.getUserObject();
2983          if(tData.getVisible()){
2984            //1) update the annotations table
2985            data.subList(tData.range.start, tData.range.end).clear();
2986            //remove the range
2987            int delta = tData.range.end - tData.range.start;
2988            //1a)first shift all following ranges
2989            Iterator rangesIter = ranges.
2990                                subList(ranges.indexOf(tData.range) + 1,
2991                                ranges.size()).
2992                                  iterator();
2993            while(rangesIter.hasNext()){
2994              Range aRange = (Range) rangesIter.next();
2995              aRange.start -= delta;
2996              aRange.end -= delta;
2997            }//while(rangesIter.hasNext())
2998            //1b)now remove the range
2999            ranges.remove(tData.range);
3000            tableChanged = true;
3001
3002            //2)update the text
3003            //hide the highlights
3004
3005            Iterator annIter = tData.getAnnotations().iterator();
3006            while(annIter.hasNext()){
3007              Annotation ann = (Annotation)annIter.next();
3008              new HighlightsRemover(ann).run();
3009            }//while(annIter.hasNext())
3010          }//if(tData.getVisible())
3011        }//while(typeNodesEnum.hasMoreElements())
3012
3013        if(tableChanged){
3014          if(annotationsTableModel != null){
3015            annotationsTableModel.fireTableDataChanged();
3016          }
3017        }//if(tableChanged)
3018
3019        //remove the node for the set
3020        typeDataMap.remove(setName);
3021        new NodeRemover(setNode).run();
3022      }//public void run()
3023
3024      String setName;
3025    }
3026
3027  }//class EventsHandler
3028
3029  /**
3030   * This class handles the blinking for the selected annotations in the
3031   * text display.
3032   */
3033  class SelectionBlinker implements Runnable{
3034    public void run(){
3035      synchronized(selectionHighlighter){
3036        highlights = selectionHighlighter.getHighlights();
3037      }
3038
3039
3040      while(highlights != null && highlights.length > 0){
3041        SwingUtilities.invokeLater(new Runnable(){
3042          public void run(){
3043            showHighlights();
3044          }
3045        });
3046        try{
3047          Thread.sleep(400);
3048        }catch(InterruptedException ie){
3049          ie.printStackTrace(Err.getPrintWriter());
3050        }
3051        SwingUtilities.invokeLater(new Runnable(){
3052          public void run(){
3053            hideHighlights();
3054          }
3055        });
3056
3057        try{
3058          Thread.sleep(600);
3059        }catch(InterruptedException ie){
3060          ie.printStackTrace(Err.getPrintWriter());
3061        }
3062        synchronized(selectionHighlighter){
3063          highlights = selectionHighlighter.getHighlights();
3064        }
3065      }//while we have highlights
3066      //no more highlights; stop the thread by exiting run();
3067      synchronized(selectionHighlighter){
3068        thread = null;
3069      }
3070    }//run()
3071
3072    /**
3073     * If there is no running thread then starts one and stores it in
3074     * the <tt>thread</tt> member.
3075     */
3076    public synchronized void testAndStart(){
3077      synchronized(selectionHighlighter){
3078        if(thread == null){
3079          thread  = new Thread(Thread.currentThread().getThreadGroup(),
3080                               this, "AnnotationEditor2");
3081          thread.setPriority(Thread.MIN_PRIORITY);
3082          thread.start();
3083        }
3084      }
3085    }
3086
3087    protected void showHighlights(){
3088      actualHighlights.clear();
3089      try{
3090        for(int i = 0; i < highlights.length; i++){
3091          actualHighlights.add(highlighter.addHighlight(
3092                                   highlights[i].getStartOffset(),
3093                                   highlights[i].getEndOffset(),
3094                                   highlights[i].getPainter()));
3095        }
3096      }catch(BadLocationException ble){
3097        ble.printStackTrace(Err.getPrintWriter());
3098      }
3099    }
3100
3101    protected void hideHighlights(){
3102      Iterator hIter = actualHighlights.iterator();
3103      while(hIter.hasNext()) highlighter.removeHighlight(hIter.next());
3104    }
3105
3106    ArrayList actualHighlights = new ArrayList();
3107    Thread thread;
3108    Highlighter.Highlight[] highlights;
3109  }//class SelectionBlinker implements Runnable
3110
3111  /**
3112   * Fixes the <a
3113   * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html">
3114   * 4406598 bug</a> in swing text components.
3115   * The bug consists in the fact that the Background attribute is ignored by
3116   * the text component whent it is defined in a style from which the current
3117   * style inherits.
3118   */
3119  public class CustomLabelView extends javax.swing.text.LabelView {
3120    public CustomLabelView(Element elem) {
3121      super(elem);
3122    }
3123
3124    public Color getBackground() {
3125      AttributeSet attr = getAttributes();
3126      if (attr != null) {
3127        javax.swing.text.Document d = super.getDocument();
3128        if (d instanceof StyledDocument){
3129          StyledDocument doc = (StyledDocument) d;
3130          return doc.getBackground(attr);
3131        }else{
3132          return null;
3133        }
3134      }
3135      return null;
3136    }
3137  }
3138
3139  /**
3140   * The popup menu items used to select annotations at right click.
3141   * Apart from the normal {@link javax.swing.JMenuItem} behaviour, this menu
3142   * item also highlits the annotation which it would select if pressed.
3143   */
3144  protected class SelectAnnotationPopupItem extends JMenuItem {
3145    public SelectAnnotationPopupItem(Annotation ann, String setName) {
3146      super(ann.getType());
3147      setToolTipText("<html><b>Features:</b><br>" +
3148                     (ann.getFeatures() == null ? "" :
3149                     ann.getFeatures().toString()) + "</html>");
3150      annotation = ann;
3151      start = ann.getStartNode().getOffset().intValue();
3152      end = ann.getEndNode().getOffset().intValue();
3153      set = setName;
3154      this.addMouseListener(new MouseAdapter() {
3155        public void mouseEntered(MouseEvent e) {
3156          try {
3157            highlight = highlighter.addHighlight(start, end,
3158                                            DefaultHighlighter.DefaultPainter);
3159          }catch(BadLocationException ble){
3160            throw new GateRuntimeException(ble.toString());
3161          }
3162        }
3163
3164        public void mouseExited(MouseEvent e) {
3165          if(highlight != null){
3166            highlighter.removeHighlight(highlight);
3167            highlight = null;
3168          }
3169        }
3170      });
3171
3172      this.addActionListener(new ActionListener() {
3173        public void actionPerformed(ActionEvent e) {
3174          Runnable runnable = new Runnable(){
3175            public void run(){
3176              if(highlight != null){
3177                highlighter.removeHighlight(highlight);
3178                highlight = null;
3179              }
3180             selectAnnotation(set, annotation);
3181            }
3182          };
3183          Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
3184                                     runnable,
3185                                     "AnnotationEditor5");
3186          thread.start();
3187        }
3188      });
3189    }
3190
3191    int start;
3192    int end;
3193    String set;
3194    Annotation annotation;
3195    Object highlight;
3196  }
3197
3198  protected class DeleteSelectedAnnotationsAction extends AbstractAction {
3199    public DeleteSelectedAnnotationsAction(JComponent source){
3200      super("Delete selected annotations");
3201      this.source = source;
3202    }
3203
3204    public void actionPerformed(ActionEvent evt){
3205      if(source == annotationsTable){
3206        //collect the list of annotations to be removed
3207        //maps from set name to list of annotations to be removed
3208        Map annotationsBySet = new HashMap();
3209        int[] rows = annotationsTable.getSelectedRows();
3210        String setName;
3211        for(int i = 0; i < rows.length; i++){
3212          int row = rows[i];
3213          //find the annotation
3214          Annotation ann = (Annotation)annotationsTable.
3215                              getModel().getValueAt(row, -1);
3216          //find the annotation set
3217          setName = (String)annotationsTable.getModel().
3218                                                      getValueAt(row, 1);
3219          java.util.List existingList = (java.util.List)
3220                                        annotationsBySet.get(setName);
3221          if(existingList == null){
3222            existingList = new ArrayList();
3223            annotationsBySet.put(setName, existingList);
3224          }
3225          existingList.add(ann);
3226        }//for(int i = 0; i < rows.length; i++)
3227        //remove the collected annotations
3228        Iterator setsIter = annotationsBySet.keySet().iterator();
3229        while(setsIter.hasNext()){
3230          setName = (String)setsIter.next();
3231          AnnotationSet set = setName.equals("Default")?
3232                              document.getAnnotations() :
3233                              document.getAnnotations(setName);
3234          set.removeAll((java.util.List)annotationsBySet.get(setName));
3235        }//while(setsIter.hasNext())
3236      }else if(source == stylesTree){
3237        TreePath[] paths = stylesTree.getSelectionPaths();
3238        for(int i = 0; i < paths.length; i++){
3239          TypeData tData = (TypeData)((DefaultMutableTreeNode)
3240                            paths[i].getLastPathComponent()).getUserObject();
3241          String setName = tData.getSet();
3242          if(tData.getType() == null){
3243            //set node
3244            if(setName.equals("Default")){
3245              JOptionPane.showMessageDialog(
3246                DocumentEditor.this,
3247                "The default annotation set cannot be deleted!\n" +
3248                "It will only be cleared...",
3249                "Gate", JOptionPane.ERROR_MESSAGE);
3250              document.getAnnotations().clear();
3251            }else{
3252              document.removeAnnotationSet(setName);
3253            }
3254          }else{
3255            //type node
3256            if(!setName.equals("Default") &&
3257               !document.getNamedAnnotationSets().containsKey(setName)){
3258              //the set for this type has already been removed completely
3259              //nothing more do (that's nice :) )
3260              return;
3261            }
3262            AnnotationSet set = setName.equals("Default") ?
3263                                document.getAnnotations() :
3264                                document.getAnnotations(setName);
3265            if(set != null){
3266              AnnotationSet subset = set.get(tData.getType());
3267              if(subset != null) set.removeAll(new ArrayList(subset));
3268            }//if(set != null)
3269          }//type node
3270        }//for(int i = 0; i < paths.length; i++)
3271      }//else if(source == stylesTree)
3272    }//public void actionPerformed(ActionEvent evt)
3273    JComponent source;
3274  }//protected class DeleteSelectedAnnotationsAction
3275
3276  /**
3277   * The action that is fired when the user wants to edit an annotation.
3278   * This will show a {@link gate.gui.AnnotationEditDialog} to allow the user
3279   * to do the editing.
3280   */
3281  protected class DumpAsXmlAction extends AbstractAction{
3282    private Set annotationsToDump = null;
3283
3284    /** Constructs an DumpAsXmlAction from an annotation and a set*/
3285    public DumpAsXmlAction(){
3286      super("Dump as XML & preserve format");
3287    }// EditAnnotationAction()
3288
3289    /** This method takes care of how the dumping is done*/
3290    public void actionPerformed(ActionEvent e){
3291      Runnable runableAction = new Runnable(){
3292        public void run(){
3293          JFileChooser fileChooser = MainFrame.getFileChooser();
3294          File selectedFile = null;
3295
3296          fileChooser.setMultiSelectionEnabled(false);
3297          fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
3298          fileChooser.setDialogTitle("Select document to save ...");
3299          fileChooser.setSelectedFiles(null);
3300
3301          int res = fileChooser.showDialog(DocumentEditor.this, "Save");
3302          if(res == JFileChooser.APPROVE_OPTION){
3303            selectedFile = fileChooser.getSelectedFile();
3304            fileChooser.setCurrentDirectory(fileChooser.getCurrentDirectory());
3305            if(selectedFile == null) return;
3306            if (myHandle!= null)
3307              myHandle.statusChanged("Please wait while dumping as XML and"+
3308              " preserving the format to " + selectedFile.toString() + " ...");
3309            // This method construct a set with all annotations that need to be
3310            // dupmped as Xml. If the set is null then only the original markups
3311            // are dumped.
3312            constructAnnotationsToDump();
3313            try{
3314              // Prepare to write into the xmlFile using UTF-8 encoding
3315              OutputStreamWriter writer = new OutputStreamWriter(
3316                                    new FileOutputStream(selectedFile),"UTF-8");
3317
3318              // Write (test the toXml() method)
3319              // This Action is added only when a gate.Document is created.
3320              // So, is for sure that the resource is a gate.Document
3321              writer.write(document.toXml(annotationsToDump));
3322              writer.flush();
3323              writer.close();
3324            } catch (Exception ex){
3325              ex.printStackTrace(Out.getPrintWriter());
3326            }// End try
3327            if (myHandle!= null)
3328              myHandle.statusChanged("Finished dumping into the "+
3329              "file : " + selectedFile.toString());
3330          }// End if
3331        }// End run()
3332      };// End Runnable
3333      Thread thread = new Thread(runableAction, "");
3334      thread.setPriority(Thread.MIN_PRIORITY);
3335      thread.start();
3336    }//public void actionPerformed(ActionEvent e)
3337
3338    /** This method constructs a set containing all annotation that user wants
3339      *  to dump as XML
3340      */
3341    private void constructAnnotationsToDump(){
3342      // Read the selected annotations and insert them into a set
3343      int[] rows = annotationsTable.getSelectedRows();
3344      if (rows.length > 0)
3345        annotationsToDump = new HashSet();
3346      for(int i = 0; i < rows.length; i++){
3347        int row = rows[i];
3348        //Find an annotation and add it to the annotationsToDump set.
3349        Annotation ann = (Annotation)annotationsTable.
3350                            getModel().getValueAt(row, -1);
3351        annotationsToDump.add(ann);
3352      }// End for
3353    }// constructAnnotationsToDump()
3354  }//class DumpAsXmlAction
3355
3356  protected class PrintAction extends AbstractAction{
3357    public PrintAction(){
3358      super("Print");
3359    }// EditAnnotationAction()
3360
3361    /** This method takes care of how the dumping is done*/
3362    public void actionPerformed(ActionEvent e){
3363      PrinterJob printerJob = PrinterJob.getPrinterJob();
3364
3365      if (printerJob.printDialog()) {
3366        try{
3367          PageFormat pageFormat = printerJob.pageDialog(printerJob.defaultPage());
3368          Printable printable = new Printable(){
3369            public int print(Graphics graphics, PageFormat pageFormat,
3370                             int pageIndex)throws PrinterException{
3371              if(pageIndex == 0){
3372                textScroll.getViewport().printAll(graphics);
3373                return Printable.PAGE_EXISTS;
3374              }else return Printable.NO_SUCH_PAGE;
3375            }
3376          };
3377          printerJob.setPrintable(printable , pageFormat);
3378          printerJob.print();
3379        }catch(Exception ex) {
3380          ex.printStackTrace();
3381        }
3382      }
3383    }
3384  }
3385
3386
3387  /**
3388   * The action that is fired when the user wants to edit an annotation.
3389   * It will build a dialog containing all the valid annotation editors.
3390   */
3391  protected class EditAnnotationAction extends AbstractAction {
3392    public EditAnnotationAction(AnnotationSet set, Annotation annotation){
3393      super("Edit");
3394      this.set = set;
3395      this.annotation = annotation;
3396      putValue(SHORT_DESCRIPTION, "Edits the annotation");
3397    }
3398
3399    public void actionPerformed(ActionEvent e){
3400      //get the list of editors
3401      java.util.List specificEditors = Gate.getCreoleRegister().
3402                                       getAnnotationVRs(annotation.getType());
3403      java.util.List genericEditors = Gate.getCreoleRegister().
3404                                      getAnnotationVRs();
3405      //create the GUI
3406      JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
3407      //add all the specific editors
3408      Iterator editorIter = specificEditors.iterator();
3409      while(editorIter.hasNext()){
3410        String editorType = (String)editorIter.next();
3411        //create the editor
3412        AnnotationVisualResource editor;
3413        try{
3414          editor = (AnnotationVisualResource)
3415                                          Factory.createResource(editorType);
3416          JScrollPane scroller = new JScrollPane((Component)editor);
3417          scroller.setPreferredSize(((Component) editor).getPreferredSize());
3418          tabbedPane.add(scroller,
3419                      ((ResourceData)Gate.getCreoleRegister().get(editorType)).
3420                                                                getName()
3421
3422                      );
3423          editor.setTarget(set);
3424          editor.setAnnotation(annotation);
3425        }catch(ResourceInstantiationException rie){
3426          rie.printStackTrace(Err.getPrintWriter());
3427        }
3428      }
3429
3430      //add all the generic editors
3431      editorIter = genericEditors.iterator();
3432      while(editorIter.hasNext()){
3433        String editorType = (String)editorIter.next();
3434        //create the editor
3435        AnnotationVisualResource editor;
3436        try{
3437          editor  = (AnnotationVisualResource)
3438                                          Factory.createResource(editorType);
3439          if(editor.canDisplayAnnotationType(annotation.getType())){
3440            editor.setTarget(set);
3441            editor.setAnnotation(annotation);
3442            tabbedPane.add(new JScrollPane((Component)editor),
3443                           ((ResourceData)Gate.getCreoleRegister().
3444                                              get(editorType)).getName());
3445          }
3446        }catch(ResourceInstantiationException rie){
3447          rie.printStackTrace(Err.getPrintWriter());
3448        }
3449
3450      }
3451
3452      //show the modal dialog until the data is OK or the user cancels
3453      boolean allOK = false;
3454      while(!allOK){
3455        if(OkCancelDialog.showDialog(DocumentEditor.this,
3456                                     tabbedPane,
3457                                     "Edit Annotation")){
3458          try{
3459            ((AnnotationVisualResource)((JScrollPane)tabbedPane.
3460                                        getSelectedComponent()).getViewport().
3461                                                                getView()
3462             ).okAction();
3463             allOK = true;
3464          }catch(GateException ge){
3465            JOptionPane.showMessageDialog(
3466              DocumentEditor.this,
3467              "There was an error:\n" +
3468              ge.toString(),
3469              "Gate", JOptionPane.ERROR_MESSAGE);
3470//            ge.printStackTrace(Err.getPrintWriter());
3471            allOK = false;
3472          }
3473        }else{
3474          if (OkCancelDialog.userHasPressedCancel)
3475            try{
3476              ((AnnotationVisualResource)((JScrollPane)tabbedPane.
3477                                        getSelectedComponent()).getViewport().
3478                                                                getView()
3479              ).cancelAction();
3480               allOK = true;
3481            } catch(GateException ge){
3482              JOptionPane.showMessageDialog(
3483                DocumentEditor.this,
3484                "There was an error:\n" +
3485                ge.toString(),
3486                "Gate", JOptionPane.ERROR_MESSAGE);
3487              allOK = false;
3488            }
3489          allOK = true;
3490        }
3491      }//while(!allOK)
3492    }//public void actionPerformed(ActionEvent e)
3493
3494    protected AnnotationSet set;
3495    protected Annotation annotation;
3496  }//class EditAnnotationAction
3497
3498  /**
3499   * The action that is fired when the user wants to create a new annotation.
3500   * It will build a dialog containing all the valid annotation editors.
3501   */
3502  class NewAnnotationAction extends AbstractAction{
3503    public NewAnnotationAction(AnnotationSet set,
3504                               Long startOffset,
3505                               Long endOffset){
3506      super("New annotation");
3507      putValue(SHORT_DESCRIPTION, "Creates a new annotation");
3508      this.set = set;
3509      this.startOffset = startOffset;
3510      this.endOffset = endOffset;
3511      this.type = null;
3512    }
3513
3514    public NewAnnotationAction(AnnotationSet set, String type,
3515                               Long startOffset, Long endOffset){
3516      super("New \"" + type + "\" annotation");
3517      putValue(SHORT_DESCRIPTION, "Creates a new annotation of type \"" +
3518                                  type + "\"");
3519      this.set = set;
3520      this.startOffset = startOffset;
3521      this.endOffset = endOffset;
3522      this.type = type;
3523    }
3524
3525    public void actionPerformed(ActionEvent e){
3526      if(set == null){
3527        //get the name from the user
3528        String setName = JOptionPane.showInputDialog(
3529              DocumentEditor.this,
3530              "Please provide a name for the new annotation set",
3531              "Gate", JOptionPane.QUESTION_MESSAGE);
3532        if(setName == null) return;
3533        this.set = document.getAnnotations(setName);
3534      }
3535      //get the lists of editors
3536      java.util.List specificEditors;
3537      if(type != null) specificEditors = Gate.getCreoleRegister().
3538                                         getAnnotationVRs(type);
3539      else specificEditors = new ArrayList();
3540
3541      java.util.List genericEditors = Gate.getCreoleRegister().
3542                                      getAnnotationVRs();
3543      //create the GUI
3544      JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
3545      //add all the specific editors
3546      Iterator editorIter = specificEditors.iterator();
3547      while(editorIter.hasNext()){
3548        String editorType = (String)editorIter.next();
3549        //create the editor
3550        AnnotationVisualResource editor;
3551        try{
3552          editor = (AnnotationVisualResource)
3553                                          Factory.createResource(editorType);
3554          tabbedPane.add(new JScrollPane((Component)editor),
3555                        ((ResourceData)Gate.getCreoleRegister().get(editorType)).
3556                                                                getName());
3557          editor.setTarget(set);
3558          editor.setSpan(startOffset, endOffset, type);
3559
3560        }catch(ResourceInstantiationException rie){
3561          rie.printStackTrace(Err.getPrintWriter());
3562        }
3563      }
3564
3565      //add all the generic editors
3566      editorIter = genericEditors.iterator();
3567      while(editorIter.hasNext()){
3568        String editorType = (String)editorIter.next();
3569        //create the editor
3570        AnnotationVisualResource editor;
3571        try{
3572          editor  = (AnnotationVisualResource)
3573                                          Factory.createResource(editorType);
3574
3575          if(type == null ||
3576             (type != null && editor.canDisplayAnnotationType(type))){
3577            editor.setTarget(set);
3578            editor.setSpan(startOffset, endOffset, type);
3579            tabbedPane.add(new JScrollPane((Component)editor),
3580                           ((ResourceData)Gate.getCreoleRegister().
3581                                              get(editorType)).getName());
3582          }
3583        }catch(ResourceInstantiationException rie){
3584          rie.printStackTrace(Err.getPrintWriter());
3585        }
3586
3587      }
3588
3589      //show the modal dialog until the data is OK or the user cancels
3590      boolean allOK = false;
3591      while(!allOK){
3592        if(OkCancelDialog.showDialog(DocumentEditor.this,
3593                                     tabbedPane, "Edit Annotation")){
3594          try{
3595            ((AnnotationVisualResource)((JScrollPane)tabbedPane.
3596                                        getSelectedComponent()).getViewport().
3597                                                                getView()
3598             ).okAction();
3599             allOK = true;
3600          }catch(GateException ge){
3601            JOptionPane.showMessageDialog(
3602              DocumentEditor.this,
3603              "There was an error:\n" +
3604              ge.toString(),
3605              "Gate", JOptionPane.ERROR_MESSAGE);
3606//            ge.printStackTrace(Err.getPrintWriter());
3607            allOK = false;
3608          }
3609        }else{
3610          allOK = true;
3611        }
3612      }//while(!allOK)
3613
3614
3615    }//public void actionPerformed(ActionEvent e)
3616
3617    AnnotationSet set;
3618    Long startOffset;
3619    Long endOffset;
3620    String type;
3621  }//class NewAnnotationAction extends AbstractAction
3622
3623  /**
3624   * Fixes the <a
3625   * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html">
3626   * 4406598 bug</a> in swing text components.
3627   * The bug consists in the fact that the Background attribute is ignored by
3628   * the text component whent it is defined in a style from which the current
3629   * style inherits.
3630   */
3631  public class CustomStyledEditorKit extends StyledEditorKit{
3632    private final ViewFactory defaultFactory = new CustomStyledViewFactory();
3633    public ViewFactory getViewFactory() {
3634      return defaultFactory;
3635    }
3636
3637    /**
3638      * Inserts content from the given stream, which will be
3639      * treated as plain text.
3640      * This insertion is done without checking \r or \r \n sequence.
3641      * It takes the text from the Reader and place it into Document at position
3642      * pos
3643      */
3644    public void read(Reader in, javax.swing.text.Document doc, int pos)
3645                throws IOException, BadLocationException {
3646
3647      char[] buff = new char[65536];
3648      int charsRead = 0;
3649      while ((charsRead = in.read(buff, 0, buff.length)) != -1) {
3650            doc.insertString(pos, new String(buff, 0, charsRead), null);
3651            pos += charsRead;
3652      }// while
3653    }// read
3654  }
3655
3656  /**
3657   * Fixes the <a
3658   * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html">
3659   * 4406598 bug</a> in swing text components.
3660   * The bug consists in the fact that the Background attribute is ignored by
3661   * the text component whent it is defined in a style from which the current
3662   * style inherits.
3663   */
3664  public class CustomStyledViewFactory implements ViewFactory{
3665    public View create(Element elem) {
3666      String kind = elem.getName();
3667      if (kind != null) {
3668        if (kind.equals(AbstractDocument.ContentElementName)) {
3669          return new CustomLabelView(elem);
3670        }else if (kind.equals(AbstractDocument.ParagraphElementName)) {
3671          return new ParagraphView(elem);
3672        }else if (kind.equals(AbstractDocument.SectionElementName)) {
3673          return new BoxView(elem, View.Y_AXIS);
3674        }else if (kind.equals(StyleConstants.ComponentElementName)) {
3675          return new ComponentView(elem);
3676        }else if (kind.equals(StyleConstants.IconElementName)) {
3677          return new IconView(elem);
3678        }
3679      }
3680      // default to text display
3681      return new CustomLabelView(elem);
3682    }
3683  }
3684  }//class AnnotationEditor