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