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