1   /*
2    * Created on Mar 23, 2004
3    *
4    * To change the template for this generated file go to
5    * Window - Preferences - Java - Code Generation - Code and Comments
6    */
7   package gate.gui.docview;
8   
9   import java.awt.*;
10  import java.awt.Color;
11  import java.awt.Component;
12  import java.awt.event.*;
13  import java.awt.event.ActionEvent;
14  import java.awt.event.ActionListener;
15  import java.io.*;
16  import java.util.*;
17  import java.util.List;
18  import java.util.prefs.Preferences;
19  
20  import javax.swing.*;
21  import javax.swing.JScrollPane;
22  import javax.swing.JTable;
23  import javax.swing.border.Border;
24  import javax.swing.event.*;
25  import javax.swing.event.MouseInputListener;
26  import javax.swing.event.PopupMenuListener;
27  import javax.swing.table.*;
28  import javax.swing.table.AbstractTableModel;
29  import javax.swing.table.TableCellRenderer;
30  import javax.swing.text.*;
31  import javax.swing.text.BadLocationException;
32  
33  import gate.*;
34  import gate.Annotation;
35  import gate.AnnotationSet;
36  import gate.event.*;
37  import gate.event.AnnotationSetEvent;
38  import gate.event.AnnotationSetListener;
39  import gate.event.DocumentEvent;
40  import gate.event.DocumentListener;
41  import gate.gui.*;
42  import gate.gui.MainFrame;
43  import gate.swing.ColorGenerator;
44  import gate.swing.XJTable;
45  import gate.util.*;
46  import gate.util.GateRuntimeException;
47  import gate.util.InvalidOffsetException;
48  
49  /**
50   * @author valyt
51   *
52   * To change the template for this generated type comment go to
53   * Window - Preferences - Java - Code Generation - Code and Comments
54   */
55  public class AnnotationSetsView extends AbstractDocumentView 
56                              implements DocumentListener,
57                                         AnnotationSetListener{
58  
59    
60    public AnnotationSetsView(){
61      setHandlers = new ArrayList();
62      tableRows = new ArrayList();
63      colourGenerator = new ColorGenerator();
64      actions = new ArrayList();
65      actions.add(new SavePreserveFormatAction());
66    }
67    
68    public List getActions() {
69      return actions;
70    }  
71  
72    /* (non-Javadoc)
73     * @see gate.gui.docview.DocumentView#getType()
74     */
75    public int getType() {
76      return VERTICAL;
77    }
78    
79    protected void initGUI() {
80      //get a pointer to the textual view used for highlights
81      Iterator centralViewsIter = owner.getCentralViews().iterator();
82      while(textView == null && centralViewsIter.hasNext()){
83        DocumentView aView = (DocumentView)centralViewsIter.next();
84        if(aView instanceof TextualDocumentView) 
85          textView = (TextualDocumentView)aView;
86      }
87      textPane = (JEditorPane)((JScrollPane)textView.getGUI())
88              .getViewport().getView();
89      
90      setHandlers.add(new SetHandler(document.getAnnotations()));
91      List setNames = document.getNamedAnnotationSets() == null ?
92              new ArrayList() :
93              new ArrayList(document.getNamedAnnotationSets().keySet());
94      Collections.sort(setNames);
95      Iterator setsIter = setNames.iterator();
96      while(setsIter.hasNext()){
97        setHandlers.add(new SetHandler(document.
98                getAnnotations((String)setsIter.next())));
99      }
100     tableRows.addAll(setHandlers);
101     mainTable = new XJTable();
102     tableModel = new SetsTableModel();
103     ((XJTable)mainTable).setSortable(false);
104     mainTable.setModel(tableModel);
105 //    mainTable.setRowMargin(0);
106 //    mainTable.getColumnModel().setColumnMargin(0);
107     SetsTableCellRenderer cellRenderer = new SetsTableCellRenderer();
108     mainTable.getColumnModel().getColumn(NAME_COL).setCellRenderer(cellRenderer);
109     mainTable.getColumnModel().getColumn(SELECTED_COL).setCellRenderer(cellRenderer);
110     SetsTableCellEditor cellEditor = new SetsTableCellEditor();
111     mainTable.getColumnModel().getColumn(SELECTED_COL).setCellEditor(cellEditor);
112     mainTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
113     mainTable.setColumnSelectionAllowed(false);
114     mainTable.setRowSelectionAllowed(true);
115     
116     mainTable.setTableHeader(null);
117     mainTable.setShowGrid(false);
118     mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
119     
120     scroller = new JScrollPane(mainTable);
121     scroller.getViewport().setOpaque(true);
122     scroller.getViewport().setBackground(mainTable.getBackground());
123     
124     annotationEditor = new AnnotationEditor(textView, this);
125     
126     mainPanel = new JPanel();
127     mainPanel.setLayout(new GridBagLayout());
128     GridBagConstraints constraints = new GridBagConstraints();
129     
130     constraints.gridy = 0;
131     constraints.gridx = GridBagConstraints.RELATIVE;
132     constraints.gridwidth = 2;
133     constraints.weighty = 1;
134     constraints.weightx = 1;
135     constraints.fill = GridBagConstraints.BOTH;
136     mainPanel.add(scroller, constraints);
137     
138     constraints.gridy = 1;
139     constraints.gridwidth = 1;
140     constraints.weighty = 0;
141     newSetNameTextField = new JTextField();
142     mainPanel.add(newSetNameTextField, constraints);
143     constraints.weightx = 0;
144     newSetAction = new NewAnnotationSetAction();
145     mainPanel.add(new JButton(newSetAction), constraints);
146     initListeners();
147   }
148   
149   public Component getGUI(){
150     return mainPanel;
151   }
152 
153   protected Color getColor(String annotationType){
154     Preferences prefRoot = Preferences.userNodeForPackage(getClass());
155     int rgba = prefRoot.getInt(annotationType, -1);
156     Color colour;
157     if(rgba == -1){
158       //initialise and save
159       float components[] = colourGenerator.getNextColor().getComponents(null);
160       colour = new Color(components[0],
161                          components[1],
162                          components[2],
163                          0.5f);
164       int rgb = colour.getRGB();
165       int alpha = colour.getAlpha();
166       rgba = rgb | (alpha << 24);
167       prefRoot.putInt(annotationType, rgba);
168     }else{
169       colour = new Color(rgba, true);
170     }
171     return colour;
172   }
173   
174   protected void saveColor(String annotationType, Color colour){
175     Preferences prefRoot = Preferences.userNodeForPackage(getClass());
176     int rgb = colour.getRGB();
177     int alpha = colour.getAlpha();
178     int rgba = rgb | (alpha << 24);
179     prefRoot.putInt(annotationType, rgba);
180   }
181   
182   /**
183    * This method will be called whenever the view becomes active. Implementers 
184    * should use this to add hooks (such as mouse listeners) to the other views
185    * as required by their functionality. 
186    */
187   protected void registerHooks(){
188     textPane.addMouseListener(textMouseListener);
189     textPane.addMouseMotionListener(textMouseListener);
190     textPane.addAncestorListener(textAncestorListener);
191   }
192 
193   /**
194    * This method will be called whenever this view becomes inactive. 
195    * Implementers should use it to unregister whatever hooks they registered
196    * in {@link #registerHooks()}.
197    *
198    */
199   protected void unregisterHooks(){
200     textPane.removeMouseListener(textMouseListener);
201     textPane.removeMouseMotionListener(textMouseListener);
202     textPane.removeAncestorListener(textAncestorListener);
203   }
204   
205   
206   protected void initListeners(){
207     document.addDocumentListener(this);
208     mainTable.addComponentListener(new ComponentAdapter(){
209       public void componentResized(ComponentEvent e){
210         //trigger a resize for the columns
211         mainTable.adjustSizes();
212 //        tableModel.fireTableRowsUpdated(0, 0);
213       }
214     });
215     
216     mainTable.addMouseListener(new MouseAdapter(){
217       public void mouseClicked(MouseEvent evt){
218         int row =  mainTable.rowAtPoint(evt.getPoint());
219         int column = mainTable.columnAtPoint(evt.getPoint());
220         if(row >= 0 && column == NAME_COL){
221           Object handler = tableRows.get(row);
222           if(handler instanceof TypeHandler){
223             TypeHandler tHandler = (TypeHandler)handler;
224             if(evt.getClickCount() >= 2){
225               //double click
226               tHandler.changeColourAction.actionPerformed(null);
227             }
228           }
229         }
230       }
231       public void mousePressed(MouseEvent evt){
232         int row =  mainTable.rowAtPoint(evt.getPoint());
233         int column = mainTable.columnAtPoint(evt.getPoint());
234         if(row >= 0 && column == NAME_COL){
235           Object handler = tableRows.get(row);
236           if(handler instanceof TypeHandler){
237             TypeHandler tHandler = (TypeHandler)handler;
238             if(evt.isPopupTrigger()){
239               //show popup
240               JPopupMenu popup = new JPopupMenu();
241               popup.add(tHandler.changeColourAction);
242               popup.show(mainTable, evt.getX(), evt.getY());
243             }
244           }
245         }
246       }
247       
248       public void mouseReleased(MouseEvent evt){
249         int row =  mainTable.rowAtPoint(evt.getPoint());
250         int column = mainTable.columnAtPoint(evt.getPoint());
251         if(row >= 0 && column == NAME_COL){
252           Object handler = tableRows.get(row);
253           if(handler instanceof TypeHandler){
254             TypeHandler tHandler = (TypeHandler)handler;
255             if(evt.isPopupTrigger()){
256               //show popup
257               JPopupMenu popup = new JPopupMenu();
258               popup.add(tHandler.changeColourAction);
259               popup.show(mainTable, evt.getX(), evt.getY());
260             }
261           }
262         }
263       }
264     });
265     
266     
267     mouseStoppedMovingAction = new MouseStoppedMovingAction();
268     mouseMovementTimer = new javax.swing.Timer(MOUSE_MOVEMENT_TIMER_DELAY, 
269             mouseStoppedMovingAction);
270     mouseMovementTimer.setRepeats(false);
271     textMouseListener = new TextMouseListener();
272     textAncestorListener = new AncestorListener(){
273       public void ancestorAdded(AncestorEvent event){
274         if(wasShowing) annotationEditor.show(false);
275         wasShowing = false;
276       }
277       
278       public void ancestorRemoved(AncestorEvent event){
279         if(annotationEditor.isShowing()){
280           wasShowing = true;
281           annotationEditor.hide();
282         }
283       }
284       
285       public void ancestorMoved(AncestorEvent event){
286         
287       }
288       private boolean wasShowing = false; 
289     };
290     
291     mainTable.getInputMap().put(
292             KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "deleteAll");
293     mainTable.getActionMap().put("deleteAll", 
294             new DeleteSelectedAnnotationGroupAction());
295     newSetNameTextField.getInputMap().put(
296             KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "newSet");
297     newSetNameTextField.getActionMap().put("newSet", newSetAction);
298   }
299     
300   
301   /* (non-Javadoc)
302    * @see gate.Resource#cleanup()
303    */
304   public void cleanup() {
305     document.removeDocumentListener(this);
306     super.cleanup();
307   }
308   
309   public void annotationSetAdded(DocumentEvent e) {
310     String newSetName = e.getAnnotationSetName();
311     SetHandler sHandler = new SetHandler(document.getAnnotations(newSetName));
312     //find the right location for the new set
313     //this is a named set and the first one is always the default one
314     int i = 1;
315     for(;
316         i < setHandlers.size() && 
317         ((SetHandler)setHandlers.get(i)).set.
318           getName().compareTo(newSetName) <= 0;
319         i++);
320     setHandlers.add(i, sHandler);
321     //update the tableRows list
322     SetHandler previousHandler = (SetHandler)setHandlers.get(i -1);
323     //find the index for the previous handler - which is guaranteed to exist
324     int j = 0;
325     for(;
326       tableRows.get(j) != previousHandler;
327       j++);
328     if(previousHandler.isExpanded()){
329       j += previousHandler.typeHandlers.size();
330     }
331     j++;
332     tableRows.add(j, sHandler);
333     //update the table view
334     tableModel.fireTableRowsInserted(j, j);
335   }//public void annotationSetAdded(DocumentEvent e) 
336   
337   public void annotationSetRemoved(DocumentEvent e) {
338     String setName = e.getAnnotationSetName();
339     //find the handler and remove it from the list of handlers
340 //    Iterator shIter = setHandlers.iterator();
341     SetHandler sHandler = getSetHandler(setName);
342     if(sHandler != null){
343       //remove highlights if any
344       Iterator typeIter = sHandler.typeHandlers.iterator();
345       while(typeIter.hasNext()){
346         TypeHandler tHandler = (TypeHandler)typeIter.next();
347         tHandler.setSelected(false);
348       }
349       setHandlers.remove(sHandler);
350       //remove the set from the table
351       int row = tableRows.indexOf(sHandler);
352       tableRows.remove(row);
353       int removed = 1;
354       //remove the type rows as well
355       if(sHandler.isExpanded())
356         for(int i = 0; i < sHandler.typeHandlers.size(); i++){ 
357           tableRows.remove(row);
358           removed++;
359         }
360       tableModel.fireTableRowsDeleted(row, row + removed -1);
361       sHandler.cleanup();
362     }
363   }//public void annotationSetRemoved(DocumentEvent e) 
364   
365   /**Called when the content of the document has changed through an edit 
366    * operation.
367    */
368   public void contentEdited(DocumentEvent e){
369     //go through all the type handlers and propagate the event
370     Iterator setIter = setHandlers.iterator();
371     while(setIter.hasNext()){
372       SetHandler sHandler = (SetHandler)setIter.next();
373       Iterator typeIter = sHandler.typeHandlers.iterator();
374       while(typeIter.hasNext()){
375         TypeHandler tHandler = (TypeHandler)typeIter.next();
376         if(tHandler.isSelected()) 
377           tHandler.repairHighlights(e.getEditStart().intValue(), 
378                   e.getEditEnd().intValue());
379       }
380     }
381   }
382   
383   
384   public void annotationAdded(AnnotationSetEvent e) {
385     AnnotationSet set = (AnnotationSet)e.getSource();
386     Annotation ann = e.getAnnotation();
387     TypeHandler tHandler = getTypeHandler(set.getName(), ann.getType());
388     if(tHandler == null){
389       //new type for this set
390       SetHandler sHandler = getSetHandler(set.getName());
391       tHandler = sHandler.newType(ann.getType());
392     }
393     tHandler.annotationAdded(ann);
394   }
395   
396   public void annotationRemoved(AnnotationSetEvent e) {
397     AnnotationSet set = (AnnotationSet)e.getSource();
398     Annotation ann = e.getAnnotation();
399     TypeHandler tHandler = getTypeHandler(set.getName(), ann.getType());
400     tHandler.annotationRemoved(ann);
401   }
402   
403   protected SetHandler getSetHandler(String name){
404     Iterator shIter = setHandlers.iterator();
405     while(shIter.hasNext()){
406       SetHandler sHandler = (SetHandler)shIter.next();
407       if(name == null){
408         if(sHandler.set.getName() == null) return sHandler;
409       }else{
410         if(name.equals(sHandler.set.getName())) return sHandler;
411       }
412     }
413     return null;
414   }
415   
416   protected TypeHandler getTypeHandler(String set, String type){
417     SetHandler sHandler = getSetHandler(set);
418     TypeHandler tHandler = null;
419     Iterator typeIter = sHandler.typeHandlers.iterator();
420     while(tHandler == null && typeIter.hasNext()){
421       TypeHandler aHandler = (TypeHandler)typeIter.next();
422       if(aHandler.name.equals(type)) tHandler = aHandler;
423     }
424     return tHandler;
425   }
426   
427   public void setTypeSelected(final String setName, 
428                               final String typeName, 
429                               final boolean selected){
430     
431     SwingUtilities.invokeLater(new Runnable(){
432       public void run(){
433         TypeHandler tHandler = getTypeHandler(setName, typeName);
434         tHandler.setSelected(selected);
435         int row = tableRows.indexOf(tHandler);
436         tableModel.fireTableRowsUpdated(row, row);
437         mainTable.getSelectionModel().setSelectionInterval(row, row);
438       }
439     });
440   }
441   
442   /**
443    * Sets the last annotation type created (which will be used as a default
444    * for creating new annotations).
445    * @param annType the type of annotation.
446    */
447   void setLastAnnotationType(String annType){
448     this.lastAnnotationType = annType;
449   }
450   
451   protected class SetsTableModel extends AbstractTableModel{
452     public int getRowCount(){
453       return tableRows.size();
454 //      //we have at least one row per set
455 //      int rows = setHandlers.size();
456 //      //expanded sets add rows
457 //      for(int i =0; i < setHandlers.size(); i++){
458 //        SetHandler sHandler = (SetHandler)setHandlers.get(i);
459 //        rows += sHandler.expanded ? sHandler.set.getAllTypes().size() : 0;
460 //      }
461 //      return rows;
462     }
463     
464     public int getColumnCount(){
465       return 2;
466     }
467     
468     public Object getValueAt(int row, int column){
469       Object value = tableRows.get(row);
470       switch(column){
471         case NAME_COL:
472           return value;
473         case SELECTED_COL:
474           if(value instanceof SetHandler)
475             return new Boolean(((SetHandler)value).isExpanded());
476           if(value instanceof TypeHandler) 
477             return new Boolean(((TypeHandler)value).isSelected());
478         default:
479           return null;
480       }
481 //      
482 //      int currentRow = 0;
483 //      Iterator handlerIter = setHandlers.iterator();
484 //      SetHandler sHandler = (SetHandler)handlerIter.next();
485 //      
486 //      while(currentRow < row){
487 //        if(sHandler.expanded){
488 //          if(sHandler.typeHandlers.size() + currentRow >= row){
489 //            //we want a row in current set
490 //             return sHandler.typeHandlers.get(row - currentRow);
491 //          }else{
492 //            currentRow += sHandler.typeHandlers.size();
493 //            sHandler = (SetHandler)handlerIter.next();
494 //          }
495 //        }else{
496 //          //just go to next handler
497 //          currentRow++;
498 //          sHandler = (SetHandler)handlerIter.next();
499 //        }
500 //        if(currentRow == row) return sHandler;
501 //      }
502 //      if(currentRow == row) return sHandler;
503 //System.out.println("BUG! row: " + row + " col: " + column);      
504 //      return null;
505     }
506     
507     public boolean isCellEditable(int rowIndex, int columnIndex){
508       Object value = tableRows.get(rowIndex);
509       switch(columnIndex){
510         case NAME_COL: return false;
511         case SELECTED_COL:
512           if(value instanceof SetHandler)
513             return ((SetHandler)value).typeHandlers.size() > 0;
514           if(value instanceof TypeHandler) return true; 
515       }
516       return columnIndex == SELECTED_COL;
517     }
518     
519     public void setValueAt(Object aValue, int rowIndex, int columnIndex){
520       Object receiver = tableRows.get(rowIndex);
521       switch(columnIndex){
522         case SELECTED_COL:
523           if(receiver instanceof SetHandler){
524             ((SetHandler)receiver).setExpanded(((Boolean)aValue).booleanValue());
525           }else if(receiver instanceof TypeHandler){
526             ((TypeHandler)receiver).setSelected(((Boolean)aValue).booleanValue());
527           }
528           
529           break;
530         default:
531           break;
532       }
533     }
534   }//public Object getValueAt(int row, int column)
535   
536   protected class SetsTableCellRenderer implements TableCellRenderer{
537     public SetsTableCellRenderer(){
538       typeLabel = new JLabel(){
539         public void repaint(long tm, int x, int y, int width, int height){}
540         public void repaint(Rectangle r){}
541         public void validate(){}
542         public void revalidate(){}
543         protected void firePropertyChange(String propertyName,
544                                           Object oldValue,
545                                           Object newValue){}
546       };
547       typeLabel.setOpaque(true);
548       typeLabel.setBorder(BorderFactory.createCompoundBorder(
549               BorderFactory.createMatteBorder(0, 5, 0, 0,
550                       mainTable.getBackground()),
551               BorderFactory.createEmptyBorder(0, 5, 0, 5)));
552 //      typeLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
553 
554       
555       setLabel = new JLabel(){
556         public void repaint(long tm, int x, int y, int width, int height){}
557         public void repaint(Rectangle r){}
558         public void validate(){}
559         public void revalidate(){}
560         protected void firePropertyChange(String propertyName,
561                                           Object oldValue,
562                                           Object newValue){}
563       };
564       setLabel.setOpaque(true);
565       setLabel.setFont(setLabel.getFont().deriveFont(Font.BOLD));
566       setLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
567       
568 
569       typeChk = new JCheckBox(){
570         public void repaint(long tm, int x, int y, int width, int height){}
571         public void repaint(Rectangle r){}
572         public void validate(){}
573         public void revalidate(){}
574         protected void firePropertyChange(String propertyName,
575                                           Object oldValue,
576                                           Object newValue){}
577       };
578       typeChk.setOpaque(true);
579 //      typeChk.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
580 
581       setChk = new JCheckBox(){
582         public void repaint(long tm, int x, int y, int width, int height){}
583         public void repaint(Rectangle r){}
584         public void validate(){}
585         public void revalidate(){}
586         protected void firePropertyChange(String propertyName,
587                                           Object oldValue,
588                                           Object newValue){}
589       };
590       setChk.setSelectedIcon(MainFrame.getIcon("expanded.gif"));
591       setChk.setIcon(MainFrame.getIcon("closed.gif"));
592       setChk.setMaximumSize(setChk.getMinimumSize());
593       setChk.setOpaque(true);
594       
595       normalBorder = BorderFactory.createLineBorder(
596               mainTable.getBackground(), 2);
597       selectedBorder = BorderFactory.createLineBorder(
598               mainTable.getSelectionBackground(), 2);
599     }
600     
601     public Component getTableCellRendererComponent(JTable table,
602                                                    Object value,
603                                                    boolean isSelected,
604                                                    boolean hasFocus,
605                                                    int row,
606                                                    int column){
607       
608       value = tableRows.get(row);
609       if(value instanceof SetHandler){
610         SetHandler sHandler = (SetHandler)value;
611         switch(column){
612           case NAME_COL:
613             setLabel.setText(sHandler.set.getName());
614             setLabel.setBackground(isSelected ?
615                                    table.getSelectionBackground() :
616                                    table.getBackground());
617             return setLabel;
618           case SELECTED_COL:
619             setChk.setSelected(sHandler.isExpanded());
620             setChk.setEnabled(sHandler.typeHandlers.size() > 0);
621             setChk.setBackground(isSelected ?
622                                  table.getSelectionBackground() :
623                                  table.getBackground());
624             return setChk;
625         }
626       }else if(value instanceof TypeHandler){
627         TypeHandler tHandler = (TypeHandler)value;
628         switch(column){
629           case NAME_COL:
630             typeLabel.setBackground(tHandler.colour);
631             typeLabel.setText(tHandler.name);
632             typeLabel.setBorder(isSelected ? selectedBorder : normalBorder);
633             return typeLabel;
634           case SELECTED_COL:
635             typeChk.setBackground(isSelected ?
636                    table.getSelectionBackground() :
637                    table.getBackground());
638             typeChk.setSelected(tHandler.isSelected());
639             return typeChk;
640         }
641       }
642       typeLabel.setText("?");
643       return typeLabel;
644       //bugcheck!
645     }
646     
647     protected JLabel typeLabel;
648     protected JLabel setLabel;
649     protected JCheckBox setChk;
650     protected JCheckBox typeChk;
651     protected Border selectedBorder;
652     protected Border normalBorder;
653   }
654   
655   protected class SetsTableCellEditor extends AbstractCellEditor
656                                       implements TableCellEditor{
657     public SetsTableCellEditor(){
658       setChk = new JCheckBox();
659       setChk.setSelectedIcon(MainFrame.getIcon("expanded.gif"));
660       setChk.setIcon(MainFrame.getIcon("closed.gif"));
661 //      setChk.setMaximumSize(setChk.getMinimumSize());
662       setChk.setOpaque(true);
663       setChk.addActionListener(new ActionListener(){
664         public void actionPerformed(ActionEvent evt){
665           fireEditingStopped();
666         }
667       });
668       typeChk = new JCheckBox();
669       typeChk.setOpaque(false);
670 //      typeChk.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
671       typeChk.addActionListener(new ActionListener(){
672         public void actionPerformed(ActionEvent evt){
673           fireEditingStopped();
674         }
675       });
676     }
677     
678     public Component getTableCellEditorComponent(JTable table,
679                                                  Object value,
680                                                  boolean isSelected,
681                                                  int row,
682                                                  int column){
683       value = tableRows.get(row);
684       if(value instanceof SetHandler){
685         SetHandler sHandler = (SetHandler)value;
686         switch(column){
687           case NAME_COL: return null;
688           case SELECTED_COL:
689             setChk.setSelected(sHandler.isExpanded());
690             setChk.setEnabled(sHandler.typeHandlers.size() > 0);
691             setChk.setBackground(isSelected ?
692                                  table.getSelectionBackground() :
693                                  table.getBackground());
694             currentChk = setChk;
695             return setChk;
696         }
697       }else if(value instanceof TypeHandler){
698         TypeHandler tHandler = (TypeHandler)value;
699         switch(column){
700           case NAME_COL: return null;
701           case SELECTED_COL:
702 //            typeChk.setBackground(tHandler.colour);
703             typeChk.setSelected(tHandler.isSelected());
704             currentChk = typeChk;
705             return typeChk;
706         }
707       }
708       return null;
709     }
710     
711     public boolean stopCellEditing(){
712       return true;
713     }
714     
715     public Object getCellEditorValue(){
716       return new Boolean(currentChk.isSelected());
717     }
718     
719     public boolean shouldSelectCell(EventObject anEvent){
720       return true;
721     }
722     
723     public boolean isCellEditable(EventObject anEvent){
724       return true;
725     }
726     
727     JCheckBox currentChk;
728     JCheckBox setChk;
729     JCheckBox typeChk;
730   }
731   
732   
733   /**
734    * Stores the data related to an annotation set
735    */
736   protected class SetHandler{
737     SetHandler(AnnotationSet set){
738       this.set = set;
739       typeHandlers = new ArrayList();
740       typeHandlersByType = new HashMap();
741       List typeNames = new ArrayList(set.getAllTypes());
742       Collections.sort(typeNames);
743       Iterator typIter = typeNames.iterator();
744       while(typIter.hasNext()){
745         String name = (String)typIter.next();
746         TypeHandler tHandler = new TypeHandler(this, name); 
747         typeHandlers.add(tHandler);
748         typeHandlersByType.put(name, tHandler);
749       }
750       set.addAnnotationSetListener(AnnotationSetsView.this);
751     }
752     
753     public void cleanup(){
754       set.removeAnnotationSetListener(AnnotationSetsView.this);
755       typeHandlers.clear();
756     }
757     
758     /**
759      * Notifies this set handler that anew type of annotations has been created
760      * @param type the new type of annotations
761      * @return the new TypeHandler created as a result
762      */
763     public TypeHandler newType(String type){
764       //create a new TypeHandler
765       TypeHandler tHandler = new TypeHandler(this, type);
766       //add it to the list at the right position
767       int pos = 0;
768       for(;
769           pos < typeHandlers.size() &&
770           ((TypeHandler)typeHandlers.get(pos)).name.compareTo(type) <= 0;
771           pos++);
772       typeHandlers.add(pos, tHandler);
773       typeHandlersByType.put(type, tHandler);
774       //preserve table selection
775       int row = mainTable.getSelectedRow();
776       int setRow = tableRows.indexOf(this);
777       if(typeHandlers.size() == 1) 
778         tableModel.fireTableRowsUpdated(setRow, setRow);
779       if(expanded){
780         tableRows.add(setRow + pos + 1, tHandler);
781         tableModel.fireTableRowsInserted(setRow + pos + 1,
782               setRow + pos + 1);
783       }
784       //restore selection if any
785       if(row != -1) mainTable.getSelectionModel().setSelectionInterval(row, row);
786       return tHandler;
787     }
788     
789     public void removeType(TypeHandler tHandler){
790       int setRow = tableRows.indexOf(this);
791       int pos = typeHandlers.indexOf(tHandler);
792       typeHandlers.remove(pos);
793       typeHandlersByType.remove(tHandler.name);
794       //preserve table selection
795       int row = mainTable.getSelectedRow();
796       if(expanded){
797         tableRows.remove(setRow + pos + 1);
798         tableModel.fireTableRowsDeleted(setRow + pos + 1, setRow + pos + 1);
799       }
800       if(typeHandlers.isEmpty()){
801         //the set has no more handlers
802         setExpanded(false);
803         tableModel.fireTableRowsUpdated(setRow, setRow);
804       }
805       //restore selection if any
806       if(row != -1) mainTable.getSelectionModel().setSelectionInterval(row, row);
807     }
808     
809     public void removeType(String type){
810       removeType((TypeHandler)typeHandlersByType.get(type));
811     }
812 
813     public TypeHandler getTypeHandler(String type){
814       return (TypeHandler)typeHandlersByType.get(type);
815     }
816     
817     public void setExpanded(boolean expanded){
818       if(this.expanded == expanded) return;
819       this.expanded = expanded;
820       int myPosition = tableRows.indexOf(this);
821       if(expanded){
822         //expand
823         tableRows.addAll(myPosition + 1, typeHandlers);
824         tableModel.fireTableRowsInserted(myPosition + 1, 
825                                          myPosition + 1 + typeHandlers.size());
826       }else{
827         //collapse
828         for(int i = 0; i < typeHandlers.size(); i++){
829           tableRows.remove(myPosition + 1);
830         }
831         tableModel.fireTableRowsDeleted(myPosition + 1, 
832                                         myPosition + 1 + typeHandlers.size());
833       }
834       tableModel.fireTableRowsUpdated(myPosition, myPosition);
835     }
836     
837     public boolean isExpanded(){
838       return expanded;
839     }
840     
841     
842     AnnotationSet set;
843     List typeHandlers;
844     Map typeHandlersByType;
845     private boolean expanded = false;
846   }
847   
848   protected class TypeHandler{
849     TypeHandler (SetHandler setHandler, String name){
850       this.setHandler = setHandler;
851       this.name = name;
852       colour = getColor(name);
853       hghltTagsForAnn = new HashMap();
854       changeColourAction = new ChangeColourAction();
855     }
856     
857     public void setColour(Color colour){
858       if(this.colour.equals(colour)) return;
859       this.colour = colour;
860       saveColor(name, colour);
861       if(isSelected()){
862         //redraw the highlights
863         Runnable runnable = new Runnable(){
864           public void run(){
865             //hide highlights
866             textView.removeHighlights(hghltTagsForAnn.values());
867             hghltTagsForAnn.clear();
868             //show highlights
869             List annots = new ArrayList(setHandler.set.get(name));
870             List tags = textView.addHighlights(annots, setHandler.set, 
871                     TypeHandler.this.colour);
872             for(int i = 0; i < annots.size(); i++){
873               hghltTagsForAnn.put(((Annotation)annots.get(i)).getId(), tags.get(i));
874             }
875           }
876         };
877         Thread thread = new Thread(runnable);
878         thread.setPriority(Thread.MIN_PRIORITY);
879         thread.start();
880       }
881       //update the table display
882       SwingUtilities.invokeLater(new Runnable(){
883         public void run(){
884           int row = tableRows.indexOf(this);
885           if(row >= 0) tableModel.fireTableRowsUpdated(row, row);
886         }
887       });
888     }
889     
890     public void setSelected(boolean selected){
891       if(this.selected == selected) return;
892       this.selected = selected;
893       final List annots = new ArrayList(setHandler.set.get(name));
894       if(selected){
895         //make sure set is expanded
896         setHandler.setExpanded(true);
897         //show highlights
898         hghltTagsForAnn.clear();
899         Iterator annIter = annots.iterator();
900         //we're doing a lot of operations so let's get out of the UI thread
901         Runnable runnable = new Runnable(){
902           public void run(){
903             //do all operations in one go
904             List tags = textView.addHighlights(annots, setHandler.set, colour);
905             for(int i = 0; i < annots.size(); i++){
906               hghltTagsForAnn.put(((Annotation)annots.get(i)).getId(), tags.get(i));
907             }
908           }
909         };
910         Thread thread = new Thread(runnable);
911         thread.setPriority(Thread.MIN_PRIORITY);
912         thread.start();
913       }else{
914         //hide highlights
915         Runnable runnable = new Runnable(){
916           public void run(){
917             //do all operations in one go
918             textView.removeHighlights(hghltTagsForAnn.values());
919             hghltTagsForAnn.clear();
920           }
921         };
922         Thread thread = new Thread(runnable);
923         thread.setPriority(Thread.MIN_PRIORITY);
924         thread.start();
925       }
926       //update the table display
927       int row = tableRows.indexOf(this);
928       tableModel.fireTableRowsUpdated(row, row);
929     }
930     
931     public boolean isSelected(){
932       return selected;
933     }
934     
935     /**
936      * Notifies this type handler that a new annotation was created of the 
937      * right type
938      * @param ann
939      */
940     public void annotationAdded(Annotation ann){
941       //if selected, add new highlight
942       if(selected) hghltTagsForAnn.put(ann.getId(), 
943               textView.addHighlight(ann, setHandler.set, colour));
944     }
945     
946     /**
947      * Notifies this type handler that an annotation has been removed
948      * @param ann the removed annotation
949      */
950     public void annotationRemoved(Annotation ann){
951       if(selected){
952         Object tag = hghltTagsForAnn.remove(ann.getId());
953         textView.removeHighlight(tag);
954       }
955       //if this was the last annotation of this type then the handler is no
956       //longer required
957       Set remainingAnns = setHandler.set.get(name); 
958       if(remainingAnns == null || remainingAnns.isEmpty()){
959         setHandler.removeType(this);
960       }
961     }
962     
963     protected void repairHighlights(int start, int end){
964       //map from tag to annotation
965       List tags = new ArrayList(hghltTagsForAnn.size());
966       List annots = new ArrayList(hghltTagsForAnn.size());
967       Iterator annIter = hghltTagsForAnn.keySet().iterator();
968       while(annIter.hasNext()){
969         Annotation ann = setHandler.set.get((Integer)annIter.next());
970         int annStart = ann.getStartNode().getOffset().intValue();
971         int annEnd = ann.getEndNode().getOffset().intValue();
972         if((annStart <= start && start <= annEnd) ||
973            (start <= annStart && annStart <= end)){
974           if(!hghltTagsForAnn.containsKey(ann.getId())){
975             System.out.println("Error!!!");
976           }
977           tags.add(hghltTagsForAnn.get(ann.getId()));
978           annots.add(ann);
979         }
980       }
981       for(int i = 0; i < tags.size(); i++){
982         Object tag = tags.get(i);
983         Annotation ann = (Annotation)annots.get(i);
984         try{
985           textView.moveHighlight(tag, 
986                   ann.getStartNode().getOffset().intValue(), 
987                   ann.getEndNode().getOffset().intValue());
988         }catch(BadLocationException ble){
989           //this should never happen as the offsets come from an annotation
990         }
991       }
992     }
993     
994     
995     protected class ChangeColourAction extends AbstractAction{
996       public ChangeColourAction(){
997         super("Change colour");
998       }
999       
1000      public void actionPerformed(ActionEvent evt){
1001        Color col = JColorChooser.showDialog(mainTable, 
1002                "Select colour for \"" + name + "\"",
1003                colour);
1004        if(col != null){
1005          Color colAlpha = new Color(col.getRed(), col.getGreen(),
1006                  col.getBlue(), 128);
1007          setColour(colAlpha);
1008        }
1009      }
1010    }
1011    
1012    ChangeColourAction changeColourAction;
1013    boolean selected;
1014    //Map from annotation ID (which is imuttable) to tag
1015    Map hghltTagsForAnn;
1016    String name;
1017    SetHandler setHandler;
1018    Color colour;
1019  }
1020  
1021  protected static class AnnotationHandler{
1022    public AnnotationHandler(AnnotationSet set, Annotation ann){
1023      this.ann = ann;
1024      this.set = set;
1025    }
1026    Annotation ann;
1027    AnnotationSet set;
1028  }
1029  
1030  /**
1031   * A mouse listener used for events in the text view. 
1032   */
1033  protected class TextMouseListener implements MouseInputListener{    
1034    public void mouseDragged(MouseEvent e){
1035      mouseStoppedMovingAction.setTextLocation(textPane.viewToModel(e.getPoint()));
1036      mouseMovementTimer.restart();
1037    }
1038    
1039    public void mouseMoved(MouseEvent e){
1040      //this triggers select annotation leading to edit annotation or new 
1041      //annotation actions
1042      //ignore if CTRL pressed
1043      if((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) return;
1044      mouseStoppedMovingAction.setTextLocation(textPane.viewToModel(e.getPoint()));
1045      mouseMovementTimer.restart();
1046    }
1047    
1048    public void mouseClicked(MouseEvent e){
1049      //this is required so we can trigger new annotation when selecting text 
1050      //by double/triple clicking
1051      mouseStoppedMovingAction.setTextLocation(textPane.viewToModel(e.getPoint()));
1052      mouseMovementTimer.restart();
1053    }
1054    
1055    public void mousePressed(MouseEvent e){
1056      
1057    }
1058    public void mouseReleased(MouseEvent e){
1059      
1060    }
1061    
1062    public void mouseEntered(MouseEvent e){
1063      
1064    }
1065    
1066    public void mouseExited(MouseEvent e){
1067      mouseMovementTimer.stop();
1068    }
1069  }//protected class TextMouseListener implements MouseInputListener
1070  
1071    
1072  protected class NewAnnotationSetAction extends AbstractAction{
1073    public NewAnnotationSetAction(){
1074      super("New");
1075      putValue(SHORT_DESCRIPTION, "Creates a new annotation set");
1076    }
1077    
1078    public void actionPerformed(ActionEvent evt){
1079      String name = newSetNameTextField.getText();
1080      newSetNameTextField.setText("");
1081      if(name != null && name.length() > 0){
1082        AnnotationSet set = document.getAnnotations(name);
1083        //select the newly added set
1084        Iterator rowsIter = tableRows.iterator();
1085        int row = -1;
1086        for(int i = 0; i < tableRows.size() && row < 0; i++){
1087          if(tableRows.get(i) instanceof SetHandler &&
1088             ((SetHandler)tableRows.get(i)).set == set) row = i;
1089        }
1090        if(row >= 0) mainTable.getSelectionModel().setSelectionInterval(row, row);
1091      }
1092    }
1093  }
1094
1095  protected class NewAnnotationAction extends AbstractAction{
1096    public void actionPerformed(ActionEvent evt){
1097      int start = textPane.getSelectionStart();
1098      int end = textPane.getSelectionEnd();
1099      if(start != end){
1100        textPane.setSelectionStart(start);
1101        textPane.setSelectionEnd(start);
1102        //create a new annotation
1103        //find the selected set
1104        int row = mainTable.getSelectedRow();
1105        //select the default annotation set if none selected
1106        if(row < 0) row = 0;
1107        //find the set handler
1108        while(!(tableRows.get(row) instanceof SetHandler)) row --;
1109        AnnotationSet set = ((SetHandler)tableRows.get(row)).set;
1110        try{
1111          Integer annId =  set.add(new Long(start), new Long(end), 
1112                  lastAnnotationType, Factory.newFeatureMap());
1113          Annotation ann = set.get(annId);
1114          //make sure new annotaion is visible
1115          setTypeSelected(set.getName(), ann.getType(), true);
1116          //show the editor
1117          annotationEditor.setAnnotation(ann, set);
1118          annotationEditor.show(true);
1119        }catch(InvalidOffsetException ioe){
1120          //this should never happen
1121          throw new GateRuntimeException(ioe);
1122        }
1123      }
1124    }
1125  }
1126  
1127  protected class SavePreserveFormatAction extends AbstractAction{
1128    public SavePreserveFormatAction(){
1129      super("Save preserving document format");
1130    }
1131    
1132    public void actionPerformed(ActionEvent evt){
1133      Runnable runableAction = new Runnable(){
1134        public void run(){
1135          JFileChooser fileChooser = MainFrame.getFileChooser();
1136          File selectedFile = null;
1137
1138          fileChooser.setMultiSelectionEnabled(false);
1139          fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
1140          fileChooser.setDialogTitle("Select document to save ...");
1141          fileChooser.setSelectedFiles(null);
1142
1143          int res = fileChooser.showDialog(owner, "Save");
1144          if(res == JFileChooser.APPROVE_OPTION){
1145            selectedFile = fileChooser.getSelectedFile();
1146            fileChooser.setCurrentDirectory(fileChooser.getCurrentDirectory());
1147            if(selectedFile == null) return;
1148            StatusListener sListener = (StatusListener)MainFrame.getListeners().
1149              get("gate.event.StatusListener");
1150            if (sListener != null) 
1151              sListener.statusChanged("Please wait while dumping annotations"+
1152              "in the original format to " + selectedFile.toString() + " ...");
1153            // This method construct a set with all annotations that need to be
1154            // dupmped as Xml. If the set is null then only the original markups
1155            // are dumped.
1156            Set annotationsToDump = new HashSet();
1157            Iterator setIter = setHandlers.iterator();
1158            while(setIter.hasNext()){
1159              SetHandler sHandler = (SetHandler)setIter.next();
1160              Iterator typeIter = sHandler.typeHandlers.iterator();
1161              while(typeIter.hasNext()){
1162                TypeHandler tHandler = (TypeHandler)typeIter.next();
1163                if(tHandler.isSelected()){
1164                  annotationsToDump.addAll(sHandler.set.get(tHandler.name));
1165                }
1166              }
1167            }
1168            
1169            try{
1170              // Prepare to write into the xmlFile using the original encoding
1171              String encoding = ((TextualDocument)document).getEncoding();
1172
1173              OutputStreamWriter writer = new OutputStreamWriter(
1174                                            new FileOutputStream(selectedFile),
1175                                            encoding);
1176
1177              //determine if the features need to be saved first
1178              Boolean featuresSaved =
1179                  Gate.getUserConfig().getBoolean(
1180                    GateConstants.SAVE_FEATURES_WHEN_PRESERVING_FORMAT);
1181              boolean saveFeatures = true;
1182              if (featuresSaved != null)
1183                saveFeatures = featuresSaved.booleanValue();
1184              // Write with the toXml() method
1185              writer.write(
1186                document.toXml(annotationsToDump, saveFeatures));
1187              writer.flush();
1188              writer.close();
1189            } catch (Exception ex){
1190              ex.printStackTrace(Out.getPrintWriter());
1191            }// End try
1192            if (sListener != null)
1193              sListener.statusChanged("Finished dumping into the "+
1194              "file : " + selectedFile.toString());
1195          }// End if
1196        }// End run()
1197      };// End Runnable
1198      Thread thread = new Thread(runableAction, "");
1199      thread.setPriority(Thread.MIN_PRIORITY);
1200      thread.start();
1201    }
1202  }
1203  
1204  /**
1205   * Used to select an annotation for editing.
1206   *
1207   */
1208  protected class MouseStoppedMovingAction extends AbstractAction{
1209    
1210    public void actionPerformed(ActionEvent evt){
1211      //first check for selection hovering
1212      //if inside selection, add new annotation.
1213      if(textPane.getSelectionStart() <= textLocation &&
1214         textPane.getSelectionEnd() >= textLocation){
1215        new NewAnnotationAction().actionPerformed(evt);
1216      }else{
1217        //now check for annotations at location
1218        List annotsAtPoint = new ArrayList();
1219        Iterator shIter = setHandlers.iterator();
1220        while(shIter.hasNext()){
1221          SetHandler sHandler = (SetHandler)shIter.next();
1222          Iterator annIter = sHandler.set.get(new Long(textLocation),
1223                                              new Long(textLocation)).iterator();
1224          while(annIter.hasNext()){
1225            Annotation ann = (Annotation)annIter.next();
1226            if(sHandler.getTypeHandler(ann.getType()).isSelected()){
1227              annotsAtPoint.add(new AnnotationHandler(sHandler.set, ann));
1228            }
1229          }
1230        }
1231        if(annotsAtPoint.size() > 0){
1232          if(annotsAtPoint.size() > 1){
1233            JPopupMenu popup = new JPopupMenu();
1234            Iterator annIter = annotsAtPoint.iterator();
1235            while(annIter.hasNext()){
1236              AnnotationHandler aHandler = (AnnotationHandler)annIter.next();
1237              popup.add(new HighlightMenuItem(
1238                      new EditAnnotationAction(aHandler),
1239                      aHandler.ann.getStartNode().getOffset().intValue(),
1240                      aHandler.ann.getEndNode().getOffset().intValue(),
1241                      popup));
1242            }
1243            try{
1244              Rectangle rect =  textPane.modelToView(textLocation);
1245              popup.show(textPane, rect.x + 10, rect.y);
1246            }catch(BadLocationException ble){
1247              throw new GateRuntimeException(ble);
1248            }
1249          }else{
1250            //only one annotation: start the editing directly
1251            new EditAnnotationAction((AnnotationHandler)annotsAtPoint.get(0)).
1252              actionPerformed(null);
1253          }
1254        }
1255      }
1256    }
1257    
1258    public void setTextLocation(int textLocation){
1259      this.textLocation = textLocation;
1260    }
1261    int textLocation;
1262  }//protected class SelectAnnotationAction extends AbstractAction{
1263  
1264  
1265  /**
1266   * The popup menu items used to select annotations
1267   * Apart from the normal {@link javax.swing.JMenuItem} behaviour, this menu
1268   * item also highlights the annotation which it would select if pressed.
1269   */
1270  protected class HighlightMenuItem extends JMenuItem {
1271    public HighlightMenuItem(Action action, int startOffset, int endOffset, 
1272            JPopupMenu popup) {
1273      super(action);
1274      this.start = startOffset;
1275      this.end = endOffset;
1276      this.addMouseListener(new MouseAdapter() {
1277        public void mouseEntered(MouseEvent e) {
1278          showHighlight();
1279        }
1280
1281        public void mouseExited(MouseEvent e) {
1282          removeHighlight();
1283        }
1284      });
1285      popup.addPopupMenuListener(new PopupMenuListener(){
1286        public void popupMenuWillBecomeVisible(PopupMenuEvent e){
1287          
1288        }
1289        public void popupMenuCanceled(PopupMenuEvent e){
1290          removeHighlight();
1291        }
1292        public void popupMenuWillBecomeInvisible(PopupMenuEvent e){
1293          removeHighlight();
1294        }
1295        
1296        
1297      });
1298    }
1299    
1300    protected void showHighlight(){
1301      try {
1302        highlight = textPane.getHighlighter().addHighlight(start, end,
1303                                        DefaultHighlighter.DefaultPainter);
1304      }catch(BadLocationException ble){
1305        throw new GateRuntimeException(ble.toString());
1306      }
1307
1308    }
1309    
1310    protected void removeHighlight(){
1311      if(highlight != null){
1312        textPane.getHighlighter().removeHighlight(highlight);
1313        highlight = null;
1314      }
1315      
1316    }
1317
1318    int start;
1319    int end;
1320    Action action;
1321    Object highlight;
1322  }
1323  
1324  
1325  
1326  protected class EditAnnotationAction extends AbstractAction{
1327    public EditAnnotationAction(AnnotationHandler aHandler){
1328      super(aHandler.ann.getType() + " [" + 
1329              (aHandler.set.getName() == null ? "  " : 
1330                aHandler.set.getName()) +
1331              "]");
1332      putValue(SHORT_DESCRIPTION, aHandler.ann.getFeatures().toString());
1333      this.aHandler = aHandler;
1334    }
1335    
1336    public void actionPerformed(ActionEvent evt){
1337      annotationEditor.setAnnotation(aHandler.ann, aHandler.set);
1338      annotationEditor.show(true);
1339    }
1340    
1341    AnnotationHandler aHandler;
1342  }
1343  
1344  protected class DeleteSelectedAnnotationGroupAction extends AbstractAction{
1345    public DeleteSelectedAnnotationGroupAction(){
1346    }
1347    public void actionPerformed(ActionEvent evt){
1348      int row = mainTable.getSelectedRow();
1349      if(row >= 0){
1350        Object handler = tableRows.get(row);
1351        if(handler instanceof TypeHandler){
1352          TypeHandler tHandler = (TypeHandler)handler;
1353          AnnotationSet set = tHandler.setHandler.set;
1354          AnnotationSet toDeleteAS = set.get(tHandler.name);
1355          if(toDeleteAS != null){
1356            List toDelete = new ArrayList(toDeleteAS);
1357            set.removeAll(toDelete);
1358          }
1359        }else if(handler instanceof SetHandler){
1360          SetHandler sHandler = (SetHandler)handler;
1361          if(sHandler.set == document.getAnnotations()){
1362            //the default annotation set - clear
1363            sHandler.set.clear();
1364          }else{
1365            document.removeAnnotationSet(sHandler.set.getName());
1366          }
1367        }
1368      }
1369    }
1370  }  
1371  
1372  List setHandlers;
1373  List tableRows; 
1374  XJTable mainTable;
1375  SetsTableModel tableModel;
1376  JScrollPane scroller;
1377  JPanel mainPanel;
1378  JTextField newSetNameTextField;
1379  
1380  TextualDocumentView textView;
1381  JEditorPane textPane;
1382  AnnotationEditor annotationEditor;
1383  NewAnnotationSetAction newSetAction;
1384  
1385  /**
1386   * The listener for mouse and mouse motion events in the text view.
1387   */
1388  protected TextMouseListener textMouseListener;
1389  
1390  protected javax.swing.Timer mouseMovementTimer;
1391  private static final int MOUSE_MOVEMENT_TIMER_DELAY = 500;
1392  protected AncestorListener textAncestorListener; 
1393  protected MouseStoppedMovingAction mouseStoppedMovingAction;
1394  
1395  protected String lastAnnotationType = "_New_";
1396  
1397  protected List actions;
1398  
1399  protected ColorGenerator colourGenerator;
1400  private static final int NAME_COL = 1;
1401  private static final int SELECTED_COL = 0;
1402  
1403}
1404