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