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