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