AnnotationSetsView.java
0001 /*
0002  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
0003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0004  *
0005  *  This file is part of GATE (see http://gate.ac.uk/), and is free
0006  *  software, licenced under the GNU Library General Public License,
0007  *  Version 2, June 1991 (in the distribution as file licence.html,
0008  *  and also available at http://gate.ac.uk/gate/licence.html).
0009  *
0010  *  Valentin Tablan, Mar 23, 2004
0011  *
0012  *  $Id: AnnotationSetsView.java 13614 2011-04-05 14:24:31Z valyt $
0013  */
0014 package gate.gui.docview;
0015 
0016 import java.awt.*;
0017 import java.awt.event.*;
0018 import java.beans.PropertyChangeEvent;
0019 import java.beans.PropertyChangeListener;
0020 import java.io.*;
0021 import java.net.URISyntaxException;
0022 import java.util.*;
0023 import java.util.List;
0024 import java.util.concurrent.BlockingQueue;
0025 import java.util.concurrent.LinkedBlockingQueue;
0026 
0027 import javax.swing.*;
0028 import javax.swing.Timer;
0029 import javax.swing.border.Border;
0030 import javax.swing.event.*;
0031 import javax.swing.table.*;
0032 import javax.swing.text.*;
0033 
0034 import gate.Annotation;
0035 import gate.AnnotationSet;
0036 import gate.Factory;
0037 import gate.Gate;
0038 import gate.GateConstants;
0039 import gate.TextualDocument;
0040 import gate.creole.ResourceData;
0041 import gate.creole.ResourceInstantiationException;
0042 import gate.event.*;
0043 import gate.event.DocumentEvent;
0044 import gate.event.DocumentListener;
0045 import gate.gui.*;
0046 import gate.gui.annedit.*;
0047 import gate.swing.ColorGenerator;
0048 import gate.swing.XJTable;
0049 import gate.swing.XJFileChooser;
0050 import gate.util.*;
0051 
0052 /**
0053  * Display document annotation sets and types in a tree view like with a table.
0054  * Allow the selection of annotation type and modification of their color.
0055  */
0056 public class AnnotationSetsView extends AbstractDocumentView 
0057                             implements DocumentListener,
0058                                        AnnotationSetListener, 
0059                                        AnnotationEditorOwner{
0060 
0061   
0062   /* (non-Javadoc)
0063    * @see gate.gui.annedit.AnnotationEditorOwner#annotationTypeChanged(gate.Annotation, java.lang.String, java.lang.String)
0064    */
0065   public void annotationChanged(Annotation ann, AnnotationSet set, 
0066           String oldType) {
0067     lastAnnotationType = ann.getType();
0068     //show new annotation type
0069     setTypeSelected(set.getName(), ann.getType()true);
0070     //select new annotation
0071 //    selectAnnotation(new AnnotationDataImpl(set, ann));
0072   }
0073   
0074   
0075 
0076   /**
0077    * Queues an an action for selecting the provided annotation
0078    */
0079   public void selectAnnotation(final AnnotationData aData) {
0080     Runnable action = new Runnable(){
0081       public void run(){
0082         List<AnnotationData> selAnns = Collections.singletonList(aData);
0083         owner.setSelectedAnnotations(selAnns);
0084       }
0085     };
0086     pendingEvents.offer(new PerformActionEvent(action));
0087     eventMinder.restart();
0088   }
0089 
0090 
0091 
0092   /* (non-Javadoc)
0093    * @see gate.gui.annedit.AnnotationEditorOwner#getNextAnnotation()
0094    */
0095   public Annotation getNextAnnotation() {
0096     return null;
0097   }
0098 
0099   /* (non-Javadoc)
0100    * @see gate.gui.annedit.AnnotationEditorOwner#getPreviousAnnotation()
0101    */
0102   public Annotation getPreviousAnnotation() {
0103     return null;
0104   }
0105 
0106   /* (non-Javadoc)
0107    * @see gate.gui.annedit.AnnotationEditorOwner#getTextComponent()
0108    */
0109   public JTextComponent getTextComponent() {
0110     return textPane;
0111   }
0112 
0113   
0114   /* (non-Javadoc)
0115    * @see gate.gui.annedit.AnnotationEditorOwner#getListComponent()
0116    * TODO: delete this obsolete method?
0117    */
0118   public AnnotationList getListComponent() {
0119     return listView;
0120   }
0121 
0122   public AnnotationSetsView(){
0123     setHandlers = new ArrayList<SetHandler>();
0124     tableRows = new ArrayList();
0125     visibleAnnotationTypes = new LinkedBlockingQueue<TypeSpec>();
0126     actions = new ArrayList();
0127     actions.add(new SavePreserveFormatAction());
0128     pendingEvents = new LinkedBlockingQueue<GateEvent>();
0129     eventMinder = new Timer(EVENTS_HANDLE_DELAY, 
0130             new HandleDocumentEventsAction());
0131     eventMinder.setRepeats(true);
0132     eventMinder.setCoalesce(true);    
0133   }
0134   
0135   public List getActions() {
0136     return actions;
0137   }  
0138 
0139   /* (non-Javadoc)
0140    * @see gate.gui.docview.DocumentView#getType()
0141    */
0142   public int getType() {
0143     return VERTICAL;
0144   }
0145   
0146   protected void initGUI(){
0147     //get a pointer to the textual view used for highlights
0148     Iterator centralViewsIter = owner.getCentralViews().iterator();
0149     while(textView == null && centralViewsIter.hasNext()){
0150       DocumentView aView = (DocumentView)centralViewsIter.next();
0151       if(aView instanceof TextualDocumentView
0152         textView = (TextualDocumentView)aView;
0153     }
0154     textPane = (JTextArea)((JScrollPane)textView.getGUI())
0155             .getViewport().getView();
0156     
0157     //get a pointer to the list view
0158     Iterator horizontalViewsIter = owner.getHorizontalViews().iterator();
0159     while(listView == null && horizontalViewsIter.hasNext()){
0160       DocumentView aView = (DocumentView)horizontalViewsIter.next();
0161       if(aView instanceof AnnotationListView
0162         listView = (AnnotationListView)aView;
0163     }
0164     //get a pointer to the stack view
0165     horizontalViewsIter = owner.getHorizontalViews().iterator();
0166     while(stackView == null && horizontalViewsIter.hasNext()){
0167       DocumentView aView = (DocumentView)horizontalViewsIter.next();
0168       if(aView instanceof AnnotationStackView)
0169         stackView = (AnnotationStackView)aView;
0170     }
0171     mainTable = new XJTable();
0172     tableModel = new SetsTableModel();
0173     mainTable.setSortable(false);
0174     mainTable.setModel(tableModel);
0175     mainTable.setRowMargin(0);
0176     mainTable.getColumnModel().setColumnMargin(0);
0177     SetsTableCellRenderer cellRenderer = new SetsTableCellRenderer();
0178     mainTable.getColumnModel().getColumn(NAME_COL).setCellRenderer(cellRenderer);
0179     mainTable.getColumnModel().getColumn(SELECTED_COL).setCellRenderer(cellRenderer);
0180     SetsTableCellEditor cellEditor = new SetsTableCellEditor();
0181     mainTable.getColumnModel().getColumn(SELECTED_COL).setCellEditor(cellEditor);
0182     mainTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
0183     mainTable.setColumnSelectionAllowed(false);
0184     mainTable.setRowSelectionAllowed(true);
0185     mainTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
0186     //block autocreation of new columns from now on
0187     mainTable.setAutoCreateColumnsFromModel(false);
0188     mainTable.setTableHeader(null);
0189     mainTable.setShowGrid(false);
0190     mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
0191     
0192     //the background colour seems to change somewhere when using the GTK+ 
0193     //look and feel on Linux, so we copy the value now and set it 
0194     Color tableBG = mainTable.getBackground();
0195     //make a copy of the value (as the reference gets changed somewhere)
0196     tableBG = new Color(tableBG.getRGB());
0197     mainTable.setBackground(tableBG);
0198     
0199     scroller = new JScrollPane(mainTable);
0200     scroller.getViewport().setOpaque(true);
0201     scroller.getViewport().setBackground(tableBG);    
0202     
0203     try {
0204       annotationEditor = createAnnotationEditor(textView, this);
0205     }
0206     catch(ResourceInstantiationException e) {
0207      //this should not really happen
0208       throw new GateRuntimeException(
0209               "Could not initialise the annotation editor!", e);
0210     }
0211     
0212     mainPanel = new JPanel();
0213     mainPanel.setLayout(new GridBagLayout());
0214     GridBagConstraints constraints = new GridBagConstraints();
0215     
0216     constraints.gridy = 0;
0217     constraints.gridx = GridBagConstraints.RELATIVE;
0218     constraints.gridwidth = 2;
0219     constraints.weighty = 1;
0220     constraints.weightx = 1;
0221     constraints.fill = GridBagConstraints.BOTH;
0222     mainPanel.add(scroller, constraints);
0223     
0224     constraints.gridy = 1;
0225     constraints.gridwidth = 1;
0226     constraints.weighty = 0;
0227     newSetNameTextField = new JTextField();
0228     mainPanel.add(newSetNameTextField, constraints);
0229     constraints.weightx = 0;
0230     newSetAction = new NewAnnotationSetAction();
0231     mainPanel.add(new JButton(newSetAction), constraints);
0232 
0233     populateUI();
0234     tableModel.fireTableDataChanged();
0235 
0236 
0237     eventMinder.start();    
0238     initListeners();
0239   }
0240   
0241   /**
0242    * Create the annotation editor (responsible for creating the window
0243    * used to edit individual annotations).
0244    
0245    @param textView
0246    @param asView
0247    @return
0248    @throws ResourceInstantiationException
0249    */
0250   protected gate.gui.annedit.OwnedAnnotationEditor createAnnotationEditor(
0251           TextualDocumentView textView, AnnotationSetsView asView)
0252           throws ResourceInstantiationException {
0253     // find the last VR that implements the AnnotationEditor interface
0254     List<String> vrTypes = new ArrayList<String>(Gate.getCreoleRegister()
0255             .getPublicVrTypes());
0256     Collections.reverse(vrTypes);
0257     for(String aVrType : vrTypes) {
0258       ResourceData rData = (ResourceData)Gate.getCreoleRegister().get(aVrType);
0259       try {
0260         Class resClass = rData.getResourceClass();
0261         if(OwnedAnnotationEditor.class.isAssignableFrom(resClass)) {
0262           OwnedAnnotationEditor newEditor = (OwnedAnnotationEditor)resClass
0263                   .newInstance();
0264           newEditor.setOwner(this);
0265           newEditor.init();
0266           return newEditor;
0267         }
0268       }
0269       catch(ClassNotFoundException cnfe) {
0270         // ignore
0271         Err.prln("Invalid CREOLE data:");
0272         cnfe.printStackTrace(Err.getPrintWriter());
0273       }
0274       catch(InstantiationException e) {
0275         e.printStackTrace();
0276       }
0277       catch(IllegalAccessException e) {
0278         e.printStackTrace();
0279       }
0280     }
0281     // if we got this far, we couldn't find an editor
0282     Err.prln("Could not find any annotation editors. Editing annotations disabled.");
0283     return null;
0284   }
0285   
0286   protected void populateUI(){
0287     setHandlers.add(new SetHandler(document.getAnnotations()));
0288     List setNames = document.getNamedAnnotationSets() == null ?
0289             new ArrayList() :
0290             new ArrayList(document.getNamedAnnotationSets().keySet());
0291     Collections.sort(setNames);
0292     Iterator setsIter = setNames.iterator();
0293     while(setsIter.hasNext()){
0294       setHandlers.add(new SetHandler(document.
0295               getAnnotations((String)setsIter.next())));
0296     }
0297     tableRows.addAll(setHandlers);
0298   }
0299   
0300   public Component getGUI(){
0301     return mainPanel;
0302   }
0303 
0304   /**
0305    * Get the saved colour for this annotation type or create a new one
0306    * and save it. The colours are saved in the user configuration file.
0307    @param annotationType type to get a colour for
0308    @return a colour
0309    */
0310   public static Color getColor(String annotationSet, String annotationType) {
0311     Map<String, String> colourMap = Gate.getUserConfig()
0312       .getMap(AnnotationSetsView.class.getName()+".colours");
0313     String colourValue = colourMap.get(annotationSet+"."+annotationType);
0314     if (colourValue == nullcolourValue = colourMap.get(annotationType);
0315 
0316     Color colour;
0317     if (colourValue == null) {
0318       float components[] = colourGenerator.getNextColor().getComponents(null);
0319       colour = new Color(components[0], components[1], components[2]0.5f);
0320       int rgb = colour.getRGB();
0321       int alpha = colour.getAlpha();
0322       int rgba = rgb | (alpha << 24);
0323       colourMap.put(annotationType, String.valueOf(rgba));
0324       Gate.getUserConfig().put(
0325         AnnotationSetsView.class.getName()+".colours", colourMap);
0326     else {
0327       colour = new Color(Integer.valueOf(colourValue)true);
0328     }
0329     
0330     return colour;
0331   }
0332   
0333   protected void saveColor(String annotationSet, String annotationType, Color colour){
0334     Map<String, String> colourMap = Gate.getUserConfig()
0335       .getMap(AnnotationSetsView.class.getName()+".colours");
0336     int rgb = colour.getRGB();
0337     int alpha = colour.getAlpha();
0338     int rgba = rgb | (alpha << 24);
0339     
0340     String defaultValue = colourMap.get(annotationType);
0341     String newValue = String.valueOf(rgba);
0342     
0343     if (newValue.equals(defaultValue)) {
0344       colourMap.remove(annotationSet+"."+annotationType);
0345     }
0346     else {
0347       colourMap.put(annotationSet+"."+annotationType, newValue);
0348     }
0349     
0350     Gate.getUserConfig().put(
0351       AnnotationSetsView.class.getName()+".colours", colourMap);
0352   }
0353 
0354   /**
0355    * Save type or remove unselected type in the preferences.
0356    @param setName set name to save/remove or null for the default set
0357    @param typeName type name to save/remove
0358    @param selected state of the selection
0359    */
0360   public void saveType(String setName, String typeName, boolean selected) {
0361     List<String> typeList = Gate.getUserConfig().getList(
0362       AnnotationSetsView.class.getName() ".types");
0363     String prefix = (setName == null"." : setName + ".";
0364     if (selected) {
0365       typeList.add(prefix+typeName);
0366     else {
0367       typeList.remove(prefix+typeName);
0368     }
0369     Gate.getUserConfig().put(
0370       AnnotationSetsView.class.getName()+".types", typeList);
0371   }
0372 
0373   /**
0374    * Restore previously selected types from the preferences.
0375    */
0376   public void restoreSavedSelectedTypes() {
0377     List<String> typeList = Gate.getUserConfig().getList(
0378       AnnotationSetsView.class.getName() ".types");
0379     for (SetHandler sHandler : setHandlers){
0380       String prefix = (sHandler.set.getName() == null?
0381         "." : sHandler.set.getName() ".";
0382       for (TypeHandler tHandler : sHandler.typeHandlers) {
0383         if (typeList.contains(prefix + tHandler.name)) {
0384           tHandler.setSelected(true);
0385         }
0386       }
0387     }
0388   }
0389 
0390   /**
0391    * Enables or disables creation of the new annotation set.
0392    */
0393   public void setNewAnnSetCreationEnabled(boolean b) {
0394     newSetAction.setEnabled(b);
0395     newSetNameTextField.setEnabled(b);
0396   }
0397 
0398   /**
0399    * This method will be called whenever the view becomes active. Implementers 
0400    * should use this to add hooks (such as mouse listeners) to the other views
0401    * as required by their functionality. 
0402    */
0403   protected void registerHooks(){
0404     textPane.addMouseListener(textMouseListener);
0405     textPane.addMouseMotionListener(textMouseListener);
0406     textPane.addPropertyChangeListener("highlighter", textChangeListener);
0407 //    textPane.addAncestorListener(textAncestorListener);
0408     restoreSelectedTypes();
0409   }
0410 
0411   /**
0412    * This method will be called whenever this view becomes inactive. 
0413    * Implementers should use it to unregister whatever hooks they registered
0414    * in {@link #registerHooks()}.
0415    *
0416    */
0417   protected void unregisterHooks(){
0418     textPane.removeMouseListener(textMouseListener);
0419     textPane.removeMouseMotionListener(textMouseListener);
0420     textPane.removePropertyChangeListener("highlighter", textChangeListener);
0421 //    textPane.removeAncestorListener(textAncestorListener);
0422     storeSelectedTypes();
0423   }
0424   
0425 
0426   /**
0427    * Populates the {@link #visibleAnnotationTypes} structure based on the 
0428    * current selection
0429    *
0430    */
0431   protected void storeSelectedTypes(){
0432     visibleAnnotationTypes.clear()// for security
0433     for(SetHandler sHandler:setHandlers){
0434       for(TypeHandler tHandler: sHandler.typeHandlers){
0435         if(tHandler.isSelected()){
0436           visibleAnnotationTypes.add(new TypeSpec(sHandler.set.getName(),
0437             tHandler.name));
0438           tHandler.setSelected(false);
0439         }
0440       }
0441     }
0442   }
0443   
0444   /**
0445    * Restores the selected types based on the state saved in the 
0446    {@link #visibleAnnotationTypes} data structure.
0447    */
0448   protected void restoreSelectedTypes(){
0449     TypeSpec typeSpec;
0450     while((typeSpec = visibleAnnotationTypes.poll()) != null){
0451       TypeHandler typeHandler =
0452         getTypeHandler(typeSpec.setName, typeSpec.type);
0453       if (typeHandler != null) { // may have been deleted since
0454         typeHandler.setSelected(true);
0455       }
0456     }
0457   }
0458 
0459   protected void initListeners(){
0460 
0461     document.addDocumentListener(this);
0462 
0463     // popup menu to change the color, select, unselect
0464     // and delete annotation types
0465     mainTable.addMouseListener(new MouseAdapter(){
0466       public void mouseClicked(MouseEvent evt){
0467         processMouseEvent(evt);
0468       }
0469       public void mousePressed(MouseEvent evt){
0470         int row =  mainTable.rowAtPoint(evt.getPoint());
0471         if(evt.isPopupTrigger()
0472         && !mainTable.isRowSelected(row)) {
0473           // if right click outside the selection then reset selection
0474           mainTable.getSelectionModel().setSelectionInterval(row, row);
0475         }
0476         processMouseEvent(evt);
0477       }
0478       public void mouseReleased(MouseEvent evt){
0479         processMouseEvent(evt);
0480       }
0481       protected void processMouseEvent(MouseEvent evt){
0482         int row = mainTable.rowAtPoint(evt.getPoint());
0483         int col = mainTable.columnAtPoint(evt.getPoint());
0484 
0485         if(row >= && col == NAME_COL){
0486           Object handler = tableRows.get(row);
0487           if(evt.isPopupTrigger()){
0488             // popup menu
0489             JPopupMenu popup = new JPopupMenu();
0490             if(handler instanceof TypeHandler
0491             && mainTable.getSelectedRowCount() == 1){
0492               TypeHandler tHandler = (TypeHandler)handler;
0493               popup.add(tHandler.changeColourAction);
0494               popup.add(new DeleteSelectedAnnotationsAction("Delete"));
0495             else if (mainTable.getSelectedRowCount() 1
0496                     || handler instanceof SetHandler) {
0497               popup.add(new SetSelectedAnnotationsAction(true));
0498               popup.add(new SetSelectedAnnotationsAction(false));
0499               popup.add(new DeleteSelectedAnnotationsAction("Delete all"));
0500             }
0501             if (popup.getComponentCount() 0) {
0502               popup.show(mainTable, evt.getX(), evt.getY());
0503             }
0504           else if(evt.getClickCount() >= 2
0505             && evt.getID() == MouseEvent.MOUSE_CLICKED
0506             && handler instanceof TypeHandler){
0507               //double click
0508               TypeHandler tHandler = (TypeHandler)handler;
0509               tHandler.changeColourAction.actionPerformed(null);
0510           }
0511         }
0512       }
0513     });
0514 
0515     // Enter key to change the color of annotation type 
0516     // Space key to select/unselect annotation type
0517     // Left/Right keys to close/open an annotation set
0518     mainTable.addKeyListener(new KeyAdapter(){
0519       public void keyPressed(KeyEvent e) {
0520         int row = mainTable.getSelectedRow();
0521         int col = mainTable.getSelectedColumn();
0522         if(row <= 0
0523         || mainTable.getSelectedRowCount() 1) {
0524           return;
0525         }
0526         Object handler = tableRows.get(row);
0527         if (col == NAME_COL) {
0528           if(e.getKeyCode() == KeyEvent.VK_ENTER
0529             && handler instanceof TypeHandler){
0530               TypeHandler tHandler = (TypeHandler)handler;
0531               tHandler.changeColourAction.actionPerformed(null);
0532             e.consume();
0533           else if (e.getKeyCode() == KeyEvent.VK_SPACE) {
0534             if (handler instanceof TypeHandler){
0535               TypeHandler tHandler = (TypeHandler)handler;
0536               (new SetSelectedAnnotationsAction(!tHandler.selected))
0537                 .actionPerformed(null);
0538             else if (handler instanceof SetHandler) {
0539               SetHandler sHandler = (SetHandler)handler;
0540               boolean allUnselected = true;
0541               for (TypeHandler tHandler : sHandler.typeHandlers) {
0542                 if (tHandler.selected) {
0543                   allUnselected = false;
0544                   break;
0545                 }
0546               }
0547               (new SetSelectedAnnotationsAction(allUnselected))
0548                 .actionPerformed(null);
0549             }
0550           else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
0551             if (handler instanceof SetHandler) {
0552               ((SetHandler)handler).setExpanded(false);
0553               mainTable.setColumnSelectionInterval(col, col);
0554               mainTable.setRowSelectionInterval(row, row);
0555             }
0556             e.consume();
0557 
0558           else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
0559             if (handler instanceof SetHandler) {
0560               ((SetHandler)handler).setExpanded(true);
0561               mainTable.setColumnSelectionInterval(col, col);
0562               mainTable.setRowSelectionInterval(row, row);
0563             }
0564             e.consume();
0565           }
0566         }
0567       }
0568     });
0569 
0570     mouseStoppedMovingAction = new MouseStoppedMovingAction();
0571     mouseMovementTimer = new javax.swing.Timer(MOUSE_MOVEMENT_TIMER_DELAY, 
0572             mouseStoppedMovingAction);
0573     mouseMovementTimer.setRepeats(false);
0574     textMouseListener = new TextMouseListener();
0575     textChangeListener = new PropertyChangeListener(){
0576       public void propertyChange(PropertyChangeEvent evt) {
0577         if(evt.getNewValue() != null){
0578           //we have a new highlighter
0579           //we need to re-highlight all selected annotations
0580           for(SetHandler sHandler : setHandlers){
0581             for(TypeHandler tHandler : sHandler.typeHandlers){
0582               if(tHandler.isSelected()){
0583                 setTypeSelected(sHandler.set.getName(), tHandler.name, false);
0584                 setTypeSelected(sHandler.set.getName(), tHandler.name, true);
0585               }
0586             }
0587           }
0588         }
0589       }
0590     };
0591     
0592     mainTable.getInputMap().put(KeyStroke.getKeyStroke("DELETE")"deleteAll");
0593     mainTable.getInputMap()
0594       .put(KeyStroke.getKeyStroke("shift DELETE")"deleteAll");
0595     mainTable.getActionMap().put("deleteAll",
0596       new DeleteSelectedAnnotationsAction("Delete"));
0597     newSetNameTextField.getInputMap().put(
0598       KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)"newSet");
0599     newSetNameTextField.getActionMap().put("newSet", newSetAction);
0600     textPane.getInputMap()
0601       .put(KeyStroke.getKeyStroke("control E")"edit annotation");
0602     textPane.getActionMap().put("edit annotation"new AbstractAction() {
0603       public void actionPerformed(ActionEvent e) {
0604         // use the same action as when the mouse stop over a selection
0605         // or annotation but this time for a keyboard shortcut
0606         mouseStoppedMovingAction.setTextLocation(textPane.getCaretPosition());
0607         mouseStoppedMovingAction.actionPerformed(null);
0608         SwingUtilities.invokeLater(new Runnable() { public void run() {
0609           annotationEditor.setPinnedMode(true);
0610         }});
0611       }});
0612 
0613     // skip first column when tabbing
0614     InputMap im =
0615       mainTable.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0616     KeyStroke tab = KeyStroke.getKeyStroke("TAB");
0617     final Action oldTabAction = mainTable.getActionMap().get(im.get(tab));
0618     Action tabAction = new AbstractAction() {
0619       public void actionPerformed(ActionEvent e) {
0620         oldTabAction.actionPerformed(e);
0621         JTable table = (JTablee.getSource();
0622         if(table.getSelectedColumn() == SELECTED_COL) {
0623           oldTabAction.actionPerformed(e);
0624         }
0625       }
0626     };
0627     mainTable.getActionMap().put(im.get(tab), tabAction);
0628     KeyStroke shiftTab = KeyStroke.getKeyStroke("shift TAB");
0629     final Action oldShiftTabAction =
0630       mainTable.getActionMap().get(im.get(shiftTab));
0631     Action shiftTabAction = new AbstractAction() {
0632       public void actionPerformed(ActionEvent e) {
0633         oldShiftTabAction.actionPerformed(e);
0634         JTable table = (JTablee.getSource();
0635         if(table.getSelectedColumn() == SELECTED_COL) {
0636           oldShiftTabAction.actionPerformed(e);
0637         }
0638       }
0639     };
0640     mainTable.getActionMap().put(im.get(shiftTab), shiftTabAction);
0641   }
0642     
0643   
0644   /* (non-Javadoc)
0645    * @see gate.Resource#cleanup()
0646    */
0647   public void cleanup() {
0648     document.removeDocumentListener(this);
0649     for(SetHandler sHandler : setHandlers){
0650       sHandler.set.removeAnnotationSetListener(AnnotationSetsView.this);
0651     }    
0652     eventMinder.stop();
0653     pendingEvents.clear();  
0654     super.cleanup();
0655     document = null;
0656   }
0657   
0658   public void annotationSetAdded(final DocumentEvent e) {
0659     pendingEvents.offer(e);
0660     eventMinder.restart();
0661   }//public void annotationSetAdded(DocumentEvent e) 
0662   
0663   public void annotationSetRemoved(final DocumentEvent e) {
0664     pendingEvents.offer(e);
0665     eventMinder.restart();
0666   }//public void annotationSetRemoved(DocumentEvent e) 
0667   
0668   /**Called when the content of the document has changed through an edit 
0669    * operation.
0670    */
0671   public void contentEdited(DocumentEvent e){
0672     //go through all the type handlers and propagate the event
0673     Iterator setIter = setHandlers.iterator();
0674     while(setIter.hasNext()){
0675       SetHandler sHandler = (SetHandler)setIter.next();
0676       Iterator typeIter = sHandler.typeHandlers.iterator();
0677       while(typeIter.hasNext()){
0678         TypeHandler tHandler = (TypeHandler)typeIter.next();
0679         if(tHandler.isSelected()) 
0680           tHandler.repairHighlights(e.getEditStart().intValue()
0681                   e.getEditEnd().intValue());
0682       }
0683     }
0684   }
0685   
0686   
0687   public void annotationAdded(final AnnotationSetEvent e) {
0688     pendingEvents.offer(e);
0689     eventMinder.restart();
0690   }
0691   
0692   public void annotationRemoved(final AnnotationSetEvent e) {
0693     pendingEvents.offer(e);
0694     eventMinder.restart();
0695   }
0696   
0697   /**
0698    * Get an annotation set handler in this annotation set view.
0699    @param name name of the annotation set or null for the default set
0700    @return the annotation set handler
0701    */
0702   protected SetHandler getSetHandler(String name){
0703     for (SetHandler setHandler : setHandlers) {
0704       if (name == null) { // default annotation set
0705         if (setHandler.set.getName() == nullreturn setHandler;
0706       else {
0707         if (name.equals(setHandler.set.getName())) return setHandler;
0708       }
0709     }
0710     // set handler not found
0711     return null;
0712   }
0713   
0714   /**
0715    * Get an annotation type handler in this annotation set view.
0716    @param set name of the annotation set or null for the default set
0717    @param type type of the annotation
0718    @return the type handler
0719    */
0720   public TypeHandler getTypeHandler(String set, String type){
0721     for(TypeHandler typeHandler : getSetHandler(set).typeHandlers){
0722       if(typeHandler.name.equals(type)) {
0723         return typeHandler;
0724       }
0725     }
0726     // type handler not found
0727     return null;
0728   }
0729 
0730   /**
0731    * Un/select an annotation type in this annotation set view
0732    * and indirectly highlight it in the document view.
0733    @param setName name of the annotation set or null for the default set
0734    @param typeName type of the annotation
0735    @param selected state of the selection
0736    */
0737   public void setTypeSelected(final String setName, 
0738                               final String typeName, 
0739                               final boolean selected){
0740     SwingUtilities.invokeLater(new Runnable(){
0741       public void run(){
0742         TypeHandler tHandler = getTypeHandler(setName, typeName);
0743         if(tHandler != null){
0744           tHandler.setSelected(selected);
0745           int row = tableRows.indexOf(tHandler);
0746           tableModel.fireTableRowsUpdated(row, row);
0747           mainTable.getSelectionModel().setSelectionInterval(row, row);
0748         }else{
0749           //type handler not created yet
0750           visibleAnnotationTypes.add(new TypeSpec(setName, typeName));  
0751         }
0752       }
0753     });
0754   }
0755   
0756  
0757   
0758   /* (non-Javadoc)
0759    * @see gate.gui.docview.AbstractDocumentView#setSelectedAnnotations(java.util.List)
0760    */
0761   @Override
0762   public void setSelectedAnnotations(List<AnnotationData> selectedAnnots) {
0763     if(annotationEditor != null && annotationEditor.isActive()){
0764       // editor active - let's update it.
0765       // For annotation editing purposes, only a single selected annotation 
0766       // makes sense. Anything else is equivalent to no selection.
0767       PerformActionEvent actionEvent = null;
0768       if(selectedAnnots.size() == 1){
0769         final AnnotationData aData = selectedAnnots.get(0);
0770         //queue the select action to the events minder
0771         actionEvent = new PerformActionEvent(new Runnable(){
0772           public void run(){
0773             //select the annotation for editing, if editing enabled
0774             if(annotationEditor.getAnnotationSetCurrentlyEdited() != 
0775                   aData.getAnnotationSet() ||
0776                annotationEditor.getAnnotationCurrentlyEdited() != 
0777                    aData.getAnnotation()){
0778               annotationEditor.editAnnotation(aData.getAnnotation(),
0779                       aData.getAnnotationSet());
0780             }
0781           }
0782         });
0783       else {
0784         actionEvent = new PerformActionEvent(new Runnable(){
0785           public void run(){
0786             // un-select the edited annotation
0787             annotationEditor.editAnnotation(null, 
0788                 annotationEditor.getAnnotationSetCurrentlyEdited());
0789           }
0790         });
0791       }
0792       pendingEvents.offer(actionEvent);
0793       eventMinder.restart();      
0794     }
0795   }
0796 
0797   /**
0798    * Sets a particular annotation as selected. If the list view is visible
0799    * and active, it makes sure that the same annotation is selected there.
0800    * If the annotation editor exists and is active, it switches it to this 
0801    * current annotation.
0802    @param ann the annotation
0803    @param annSet the parent set
0804    */
0805   public void selectAnnotation(final Annotation ann, 
0806           final AnnotationSet annSet){
0807     selectAnnotation(new AnnotationDataImpl(annSet, ann));
0808   }
0809   
0810   protected class SetsTableModel extends AbstractTableModel{
0811     public int getRowCount(){
0812       return tableRows.size();
0813 //      //we have at least one row per set
0814 //      int rows = setHandlers.size();
0815 //      //expanded sets add rows
0816 //      for(int i =0; i < setHandlers.size(); i++){
0817 //        SetHandler sHandler = (SetHandler)setHandlers.get(i);
0818 //        rows += sHandler.expanded ? sHandler.set.getAllTypes().size() : 0;
0819 //      }
0820 //      return rows;
0821     }
0822     
0823     public int getColumnCount(){
0824       return 2;
0825     }
0826     
0827     public Object getValueAt(int row, int column){
0828       Object value = tableRows.get(row);
0829       switch(column){
0830         case NAME_COL:
0831           return value;
0832         case SELECTED_COL:
0833           if(value instanceof SetHandler)
0834             return new Boolean(((SetHandler)value).isExpanded());
0835           if(value instanceof TypeHandler
0836             return new Boolean(((TypeHandler)value).isSelected());
0837         default:
0838           return null;
0839       }
0840 //      
0841 //      int currentRow = 0;
0842 //      Iterator handlerIter = setHandlers.iterator();
0843 //      SetHandler sHandler = (SetHandler)handlerIter.next();
0844 //      
0845 //      while(currentRow < row){
0846 //        if(sHandler.expanded){
0847 //          if(sHandler.typeHandlers.size() + currentRow >= row){
0848 //            //we want a row in current set
0849 //             return sHandler.typeHandlers.get(row - currentRow);
0850 //          }else{
0851 //            currentRow += sHandler.typeHandlers.size();
0852 //            sHandler = (SetHandler)handlerIter.next();
0853 //          }
0854 //        }else{
0855 //          //just go to next handler
0856 //          currentRow++;
0857 //          sHandler = (SetHandler)handlerIter.next();
0858 //        }
0859 //        if(currentRow == row) return sHandler;
0860 //      }
0861 //      if(currentRow == row) return sHandler;
0862 //System.out.println("BUG! row: " + row + " col: " + column);      
0863 //      return null;
0864     }
0865     
0866     public boolean isCellEditable(int rowIndex, int columnIndex){
0867       Object value = tableRows.get(rowIndex);
0868       switch(columnIndex){
0869         case NAME_COL: return false;
0870         case SELECTED_COL:
0871           if(value instanceof SetHandler)
0872             return ((SetHandler)value).typeHandlers.size() 0;
0873           if(value instanceof TypeHandlerreturn true
0874       }
0875       return columnIndex == SELECTED_COL;
0876     }
0877 
0878     public void setValueAt(Object aValue, int rowIndex, int columnIndex){
0879       Object receiver = tableRows.get(rowIndex);
0880       switch(columnIndex){
0881         case SELECTED_COL:
0882           if(receiver instanceof SetHandler){
0883             ((SetHandler)receiver).setExpanded(((Boolean)aValue).booleanValue());
0884           }else if(receiver instanceof TypeHandler){
0885             ((TypeHandler)receiver).setSelected(((Boolean)aValue).booleanValue());
0886           }
0887           
0888           break;
0889         default:
0890           break;
0891       }
0892     }
0893   }//public Object getValueAt(int row, int column)
0894   
0895   protected class SetsTableCellRenderer implements TableCellRenderer{
0896     public SetsTableCellRenderer(){
0897       typeLabel = new JLabel(){
0898         public void repaint(long tm, int x, int y, int width, int height){}
0899         public void repaint(Rectangle r){}
0900         public void validate(){}
0901         public void revalidate(){}
0902         protected void firePropertyChange(String propertyName,
0903                                           Object oldValue,
0904                                           Object newValue){}
0905       };
0906       typeLabel.setOpaque(true);
0907       typeLabel.setBorder(BorderFactory.createCompoundBorder(
0908               BorderFactory.createMatteBorder(0500,
0909                       mainTable.getBackground()),
0910               BorderFactory.createEmptyBorder(0505)));
0911 //      typeLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
0912 
0913       
0914       setLabel = new JLabel(){
0915         public void repaint(long tm, int x, int y, int width, int height){}
0916         public void repaint(Rectangle r){}
0917         public void validate(){}
0918         public void revalidate(){}
0919         protected void firePropertyChange(String propertyName,
0920                                           Object oldValue,
0921                                           Object newValue){}
0922       };
0923       setLabel.setOpaque(true);
0924       setLabel.setFont(setLabel.getFont().deriveFont(Font.BOLD));
0925       setLabel.setBorder(BorderFactory.createEmptyBorder(0505));
0926       
0927 
0928       typeChk = new JCheckBox(){
0929         public void repaint(long tm, int x, int y, int width, int height){}
0930         public void repaint(Rectangle r){}
0931         public void validate(){}
0932         public void revalidate(){}
0933         protected void firePropertyChange(String propertyName,
0934                                           Object oldValue,
0935                                           Object newValue){}
0936       };
0937       typeChk.setOpaque(true);
0938 //      typeChk.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
0939 
0940       setChk = new JCheckBox(){
0941         public void repaint(long tm, int x, int y, int width, int height){}
0942         public void repaint(Rectangle r){}
0943         public void validate(){}
0944         public void revalidate(){}
0945         protected void firePropertyChange(String propertyName,
0946                                           Object oldValue,
0947                                           Object newValue){}
0948       };
0949       setChk.setSelectedIcon(MainFrame.getIcon("expanded"));
0950       setChk.setIcon(MainFrame.getIcon("closed"));
0951       setChk.setMaximumSize(setChk.getMinimumSize());
0952       setChk.setOpaque(true);
0953       
0954       normalBorder = BorderFactory.createLineBorder(
0955               mainTable.getBackground()2);
0956       selectedBorder = BorderFactory.createLineBorder(
0957               mainTable.getSelectionBackground()2);
0958     }
0959     
0960     public Component getTableCellRendererComponent(JTable table,
0961                                                    Object value,
0962                                                    boolean isSelected,
0963                                                    boolean hasFocus,
0964                                                    int row,
0965                                                    int column){
0966 
0967       value = tableRows.get(row);
0968       if(value instanceof SetHandler){
0969         SetHandler sHandler = (SetHandler)value;
0970         switch(column){
0971           case NAME_COL:
0972             setLabel.setText(sHandler.set.getName());
0973             setLabel.setBackground(isSelected ?
0974                                    table.getSelectionBackground() :
0975                                    table.getBackground());
0976             return setLabel;
0977           case SELECTED_COL:
0978             setChk.setSelected(sHandler.isExpanded());
0979             setChk.setBackground(isSelected ?
0980                                  table.getSelectionBackground() :
0981                                  table.getBackground());
0982             setChk.setEnabled(sHandler.typeHandlers.size() 0);            
0983             return setChk;
0984         }
0985       }else if(value instanceof TypeHandler){
0986         TypeHandler tHandler = (TypeHandler)value;
0987         switch(column){
0988           case NAME_COL:
0989             typeLabel.setBackground(tHandler.colour);
0990             typeLabel.setText(tHandler.name);
0991             typeLabel.setBorder(isSelected ? selectedBorder : normalBorder);
0992             return typeLabel;
0993           case SELECTED_COL:
0994             typeChk.setBackground(isSelected ?
0995                     table.getSelectionBackground() :
0996                    table.getBackground());
0997             typeChk.setSelected(tHandler.isSelected());
0998             return typeChk;
0999         }
1000       }
1001       typeLabel.setText("?");
1002       return typeLabel;
1003       //bugcheck!
1004     }
1005     
1006     protected JLabel typeLabel;
1007     protected JLabel setLabel;
1008     protected JCheckBox setChk;
1009     protected JCheckBox typeChk;
1010     protected Border selectedBorder;
1011     protected Border normalBorder;
1012   }
1013   
1014   protected class SetsTableCellEditor extends AbstractCellEditor
1015                                       implements TableCellEditor{
1016     public SetsTableCellEditor(){
1017       setChk = new JCheckBox();
1018       setChk.setSelectedIcon(MainFrame.getIcon("expanded"));
1019       setChk.setIcon(MainFrame.getIcon("closed"));
1020 //      setChk.setMaximumSize(setChk.getMinimumSize());
1021       setChk.setOpaque(true);
1022       setChk.addActionListener(new ActionListener(){
1023         public void actionPerformed(ActionEvent evt){
1024           fireEditingStopped();
1025         }
1026       });
1027       typeChk = new JCheckBox();
1028       typeChk.setOpaque(false);
1029 //      typeChk.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
1030       typeChk.addActionListener(new ActionListener(){
1031         public void actionPerformed(ActionEvent evt){
1032           fireEditingStopped();
1033         }
1034       });
1035     }
1036     
1037     public Component getTableCellEditorComponent(JTable table,
1038                                                  Object value,
1039                                                  boolean isSelected,
1040                                                  int row,
1041                                                  int column){
1042       value = tableRows.get(row);
1043       if(value instanceof SetHandler){
1044         SetHandler sHandler = (SetHandler)value;
1045         switch(column){
1046           case NAME_COL: return null;
1047           case SELECTED_COL:
1048             setChk.setSelected(sHandler.isExpanded());
1049             setChk.setEnabled(sHandler.typeHandlers.size() 0);
1050             setChk.setBackground(isSelected ?
1051                                    table.getSelectionBackground() :
1052                                    table.getBackground());
1053             currentChk = setChk;
1054             return setChk;
1055         }
1056       }else if(value instanceof TypeHandler){
1057         TypeHandler tHandler = (TypeHandler)value;
1058         switch(column){
1059           case NAME_COL: return null;
1060           case SELECTED_COL:
1061 //            typeChk.setBackground(tHandler.colour);
1062             typeChk.setSelected(tHandler.isSelected());
1063             currentChk = typeChk;
1064             return typeChk;
1065         }
1066       }
1067       return null;
1068     }
1069     
1070     public boolean stopCellEditing(){
1071       return true;
1072     }
1073     
1074     public Object getCellEditorValue(){
1075       return new Boolean(currentChk.isSelected());
1076     }
1077     
1078     public boolean shouldSelectCell(EventObject anEvent){
1079       return false;
1080     }
1081     
1082     public boolean isCellEditable(EventObject anEvent){
1083       return true;
1084     }
1085     
1086     JCheckBox currentChk;
1087     JCheckBox setChk;
1088     JCheckBox typeChk;
1089   }
1090   
1091   
1092   /**
1093    * Stores the data related to an annotation set
1094    */
1095   public class SetHandler{
1096     SetHandler(AnnotationSet set){
1097       this.set = set;
1098       typeHandlers = new ArrayList<TypeHandler>();
1099       typeHandlersByType = new HashMap();
1100       List typeNames = new ArrayList(set.getAllTypes());
1101       Collections.sort(typeNames);
1102       Iterator typIter = typeNames.iterator();
1103       while(typIter.hasNext()){
1104         String name = (String)typIter.next();
1105         TypeHandler tHandler = new TypeHandler(this, name);
1106         tHandler.annotationCount = set.get(name).size();
1107         typeHandlers.add(tHandler);
1108         typeHandlersByType.put(name, tHandler);
1109       }
1110       set.addAnnotationSetListener(AnnotationSetsView.this);
1111     }
1112     
1113     public void cleanup(){
1114       set.removeAnnotationSetListener(AnnotationSetsView.this);
1115       typeHandlers.clear();
1116     }
1117     
1118     /**
1119      * Notifies this set handler that a new type of annotations has been created
1120      @param type the new type of annotations
1121      @return the new TypeHandler created as a result
1122      */
1123     public TypeHandler newType(String type){
1124       //create a new TypeHandler
1125       TypeHandler tHandler = new TypeHandler(this, type);
1126       //add it to the list at the right position
1127       int pos = 0;
1128       for(;
1129           pos < typeHandlers.size() &&
1130           ((TypeHandler)typeHandlers.get(pos)).name.compareTo(type<= 0;
1131           pos++);
1132       typeHandlers.add(pos, tHandler);
1133       typeHandlersByType.put(type, tHandler);
1134       //preserve table selection
1135       int row = mainTable.getSelectedRow();
1136       int setRow = tableRows.indexOf(this);
1137       if(typeHandlers.size() == 1
1138         tableModel.fireTableRowsUpdated(setRow, setRow);
1139       if(expanded){
1140         tableRows.add(setRow + pos + 1, tHandler);
1141         tableModel.fireTableRowsInserted(setRow + pos + 1,
1142               setRow + pos + 1);
1143       }
1144       //restore selection if any
1145       if(row != -1mainTable.getSelectionModel().setSelectionInterval(row, row);
1146       //select the newly created type if previously requested
1147       TypeSpec typeSpec = new TypeSpec(set.getName(), type);
1148       if(visibleAnnotationTypes.remove(typeSpec)){
1149         tHandler.setSelected(true);
1150       }
1151       return tHandler;
1152     }
1153     
1154     public void removeType(TypeHandler tHandler){
1155       int setRow = tableRows.indexOf(this);
1156       int pos = typeHandlers.indexOf(tHandler);
1157       if(setRow != -&& pos != -1){
1158         typeHandlers.remove(pos);
1159         typeHandlersByType.remove(tHandler.name);
1160         //preserve table selection
1161         int row = mainTable.getSelectedRow();
1162         if(expanded){
1163           tableRows.remove(setRow + pos + 1);
1164           tableModel.fireTableRowsDeleted(setRow + pos + 1, setRow + pos + 1);
1165 //          if(row >= (setRow + pos + 1)) row--;
1166         }
1167         if(typeHandlers.isEmpty()){
1168           //the set has no more handlers
1169           setExpanded(false);
1170           tableModel.fireTableRowsUpdated(setRow, setRow);
1171         }
1172         //restore selection if any
1173         if(row != -1){
1174           if(mainTable.getRowCount() <= row){
1175             row = mainTable.getRowCount() -1;
1176           }
1177           mainTable.getSelectionModel().setSelectionInterval(row, row);        }
1178       }
1179     }
1180     
1181     public void removeType(String type){
1182       removeType((TypeHandler)typeHandlersByType.get(type));
1183     }
1184 
1185     public TypeHandler getTypeHandler(String type){
1186       return (TypeHandler)typeHandlersByType.get(type);
1187     }
1188     
1189     public void setExpanded(boolean expanded){
1190       if(this.expanded == expandedreturn;
1191       this.expanded = expanded;
1192       int myPosition = tableRows.indexOf(this);
1193       if(expanded){
1194         //expand
1195         tableRows.addAll(myPosition + 1, typeHandlers);
1196         tableModel.fireTableRowsInserted(myPosition + 1
1197                                           myPosition + + typeHandlers.size());
1198       }else{
1199         //collapse
1200         for(int i = 0; i < typeHandlers.size(); i++){
1201           tableRows.remove(myPosition + 1);
1202         }
1203         tableModel.fireTableRowsDeleted(myPosition + 1
1204                                         myPosition + + typeHandlers.size());
1205       }
1206       tableModel.fireTableRowsUpdated(myPosition, myPosition);
1207     }
1208     
1209     public boolean isExpanded(){
1210       return expanded;
1211     }
1212     
1213     
1214     AnnotationSet set;
1215     List<TypeHandler> typeHandlers;
1216     Map typeHandlersByType;
1217     private boolean expanded = false;
1218   }
1219   
1220   public class TypeHandler{
1221     TypeHandler (SetHandler setHandler, String name){
1222       this.setHandler = setHandler;
1223       this.name = name;
1224       colour = getColor(setHandler.set.getName(),name);
1225       hghltTagsForAnnId = new HashMap<Integer, Object>();
1226       annListTagsForAnn = new HashMap();
1227       changeColourAction = new ChangeColourAction();
1228       annotationCount = 0;
1229     }
1230     
1231     /**
1232      @return the colour
1233      */
1234     public Color getColour() {
1235       return colour;
1236     }
1237 
1238     public void setColour(Color colour){
1239       if(this.colour.equals(colour)) return;
1240       this.colour = colour;
1241       saveColor(setHandler.set.getName(),name, colour);
1242       if(isSelected()){
1243         //redraw the highlights
1244         //hide highlights
1245         textView.removeHighlights(hghltTagsForAnnId.values());
1246         hghltTagsForAnnId.clear();
1247         //show highlights
1248         List<Annotation> annots = new ArrayList<Annotation>(
1249                 setHandler.set.get(name));
1250         List<AnnotationData>aDataList = new ArrayList<AnnotationData>();
1251         for(Annotation ann : annots){
1252           aDataList.add(new AnnotationDataImpl(setHandler.set, ann));
1253         }
1254         List tags = textView.addHighlights(aDataList, TypeHandler.this.colour);
1255         for(int i = 0; i < annots.size(); i++){
1256           hghltTagsForAnnId.put(annots.get(i).getId(), tags.get(i));
1257         }
1258       }
1259       //update the table display
1260       int row = tableRows.indexOf(this);
1261       if(row >= 0tableModel.fireTableRowsUpdated(row, row);
1262       
1263       if (stackView.isActive()) stackView.updateStackView();
1264     }
1265     
1266     public void setSelected(boolean selected){
1267       if(this.selected == selectedreturn;
1268       this.selected = selected;
1269       final List<Annotation> annots = new ArrayList<Annotation>(setHandler.set.get(name));
1270       if(selected){
1271         //make sure set is expanded
1272         setHandler.setExpanded(true);
1273         //add to the list view
1274         annListTagsForAnn.clear();
1275         List<AnnotationData> listTags = 
1276             listView.addAnnotations(annots, setHandler.set);
1277         for(AnnotationData aData: listTags)
1278           annListTagsForAnn.put(aData.getAnnotation().getId(), aData);
1279         //show highlights
1280         hghltTagsForAnnId.clear();
1281 //        List tags = textView.addHighlights(annots, setHandler.set, colour);
1282         List tags = textView.addHighlights(listTags, colour);
1283         for(int i = 0; i < annots.size(); i++){
1284           hghltTagsForAnnId.put(annots.get(i).getId(), tags.get(i));
1285         }
1286       }else{
1287         //hide highlights
1288         try{
1289           listView.removeAnnotations(annListTagsForAnn.values());
1290           textView.removeHighlights(hghltTagsForAnnId.values());
1291         }finally{
1292           hghltTagsForAnnId.clear();
1293           annListTagsForAnn.clear();
1294         }
1295       }
1296       //update the table display
1297       int row = tableRows.indexOf(this);
1298       tableModel.fireTableRowsUpdated(row, row);
1299       saveType(setHandler.set.getName(), name, selected);
1300       //update the stack view
1301       stackView.updateStackView();
1302     }
1303     
1304     public boolean isSelected(){
1305       return selected;
1306     }
1307     
1308     /**
1309      * Notifies this type handler that a new annotation was created of the 
1310      * right type
1311      @param ann
1312      */
1313     public void annotationAdded(final Annotation ann){
1314       annotationCount++;
1315       if(selected){
1316         //add new highlight
1317         if(!hghltTagsForAnnId.containsKey(ann.getId())) 
1318             hghltTagsForAnnId.put(ann.getId()
1319                     textView.addHighlight(
1320                             new AnnotationDataImpl(setHandler.set, ann),
1321                             colour));
1322         if(!annListTagsForAnn.containsKey(ann.getId())){
1323           annListTagsForAnn.put(ann.getId()
1324               listView.addAnnotation(ann, setHandler.set));
1325         }
1326         //update the stack view
1327         stackView.updateStackView();
1328       }
1329     }
1330     
1331     /**
1332      * Notifies this type handler that an annotation has been removed
1333      @param ann the removed annotation
1334      */
1335     public void annotationRemoved(Annotation ann){
1336       annotationCount--;
1337       if(selected){
1338         //single annotation removal
1339         Object tag = hghltTagsForAnnId.remove(ann.getId());
1340         if(tag != nulltextView.removeHighlight(tag);
1341         AnnotationData listTag = annListTagsForAnn.remove(ann.getId());
1342         if(tag != nulllistView.removeAnnotation(listTag);
1343         //update the stack view
1344         stackView.updateStackView();
1345       }
1346       //if this was the last annotation of this type then the handler is no
1347       //longer required
1348       if(annotationCount == 0){
1349         setHandler.removeType(TypeHandler.this);
1350       }      
1351     }
1352     
1353     protected void repairHighlights(int start, int end){
1354       //map from tag to annotation
1355       List tags = new ArrayList(hghltTagsForAnnId.size());
1356       List<Annotation> annots = new ArrayList<Annotation>(hghltTagsForAnnId.size());
1357       Iterator annIter = hghltTagsForAnnId.keySet().iterator();
1358       while(annIter.hasNext()){
1359         Annotation ann = setHandler.set.get((Integer)annIter.next());
1360         // editing the text sometimes leads to annotations being deleted 
1361         if(ann == nullcontinue;
1362         int annStart = ann.getStartNode().getOffset().intValue();
1363         int annEnd = ann.getEndNode().getOffset().intValue();
1364         if((annStart <= start && start <= annEnd||
1365            (start <= annStart && annStart <= end)){
1366           if(!hghltTagsForAnnId.containsKey(ann.getId())){
1367             System.out.println("Error!!!");
1368           }
1369           tags.add(hghltTagsForAnnId.get(ann.getId()));
1370           annots.add(ann);
1371         }
1372       }
1373       for(int i = 0; i < tags.size(); i++){
1374         Object tag = tags.get(i);
1375         Annotation ann = annots.get(i);
1376         try{
1377           textView.moveHighlight(tag, 
1378                   ann.getStartNode().getOffset().intValue()
1379                   ann.getEndNode().getOffset().intValue());
1380         }catch(BadLocationException ble){
1381           //this should never happen as the offsets come from an annotation
1382         }
1383       }
1384     }
1385     
1386     
1387     protected class ChangeColourAction extends AbstractAction{
1388       public ChangeColourAction(){
1389         super("Change colour");
1390         putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("ENTER"));
1391       }
1392       
1393       public void actionPerformed(ActionEvent evt){
1394         Color col = JColorChooser.showDialog(mainTable, 
1395                 "Select colour for \"" + name + "\"",
1396                 colour);
1397         if(col != null){
1398           Color colAlpha = new Color(col.getRed(), col.getGreen(),
1399                   col.getBlue()128);
1400           setColour(colAlpha);
1401         }
1402       }
1403     }
1404     
1405     ChangeColourAction changeColourAction;
1406     boolean selected;
1407     /**
1408      * Map from annotation ID (which is immutable) to highlight tag
1409      */
1410     Map<Integer, Object> hghltTagsForAnnId;
1411 
1412     /**
1413      * Map from annotation ID (which is immutable) to AnnotationListView tag
1414      */
1415     Map<Integer, AnnotationData> annListTagsForAnn;
1416     
1417     String name;
1418     SetHandler setHandler;
1419     Color colour;
1420     int annotationCount;
1421   }
1422   
1423   /**
1424    * A class storing the identifying information for an annotation type (i.e.
1425    * the set name and the type).
1426    @author Valentin Tablan (valyt)
1427    *
1428    */
1429   private static class TypeSpec{
1430     private String setName;
1431     
1432     private String type;
1433 
1434     public TypeSpec(String setName, String type) {
1435       super();
1436       this.setName = setName;
1437       this.type = type;
1438     }
1439 
1440     @Override
1441     public int hashCode() {
1442       final int PRIME = 31;
1443       int result = 1;
1444       result = PRIME * result + ((setName == null: setName.hashCode());
1445       result = PRIME * result + ((type == null: type.hashCode());
1446       return result;
1447     }
1448 
1449     @Override
1450     public boolean equals(Object obj) {
1451       if(this == objreturn true;
1452       if(obj == nullreturn false;
1453       if(getClass() != obj.getClass()) return false;
1454       final TypeSpec other = (TypeSpec)obj;
1455       if(setName == null) {
1456         if(other.setName != nullreturn false;
1457       }
1458       else if(!setName.equals(other.setName)) return false;
1459       if(type == null) {
1460         if(other.type != nullreturn false;
1461       }
1462       else if(!type.equals(other.type)) return false;
1463       return true;
1464     }
1465   }
1466   
1467 
1468   /**
1469    * A mouse listener used for events in the text view. 
1470    */
1471   protected class TextMouseListener implements MouseInputListener{    
1472     public void mouseDragged(MouseEvent e){
1473       //do not create annotations while dragging
1474       mouseMovementTimer.stop();
1475     }
1476     
1477     public void mouseMoved(MouseEvent e){
1478       //this triggers select annotation leading to edit annotation or new 
1479       //annotation actions
1480       //ignore movement if CTRL pressed or dragging
1481       int modEx = e.getModifiersEx();
1482       if((modEx & MouseEvent.CTRL_DOWN_MASK!= 0){
1483         mouseMovementTimer.stop();
1484         return;
1485       }
1486       if((modEx & MouseEvent.BUTTON1_DOWN_MASK!= 0){
1487         mouseMovementTimer.stop();
1488         return;
1489       }
1490       //check the text location is real
1491       int textLocation = textPane.viewToModel(e.getPoint());
1492       try {
1493         Rectangle viewLocation = textPane.modelToView(textLocation);
1494         //expand the rectangle a bit
1495         int error = 10;
1496         viewLocation = new Rectangle(viewLocation.x - error, 
1497                                      viewLocation.y - error,
1498                                      viewLocation.width + 2*error, 
1499                                      viewLocation.height + 2*error);
1500         if(viewLocation.contains(e.getPoint())){
1501           mouseStoppedMovingAction.setTextLocation(textLocation);
1502         }else{
1503           mouseStoppedMovingAction.setTextLocation(-1);
1504         }
1505       }
1506       catch(BadLocationException e1) {
1507         //this should not happen, as the text location comes from the text view
1508         //if it does. we'll just ignore it.
1509 //        throw new LuckyException(e1);
1510       }finally{
1511         mouseMovementTimer.restart();
1512       }
1513     }
1514     
1515     public void mouseClicked(MouseEvent e){
1516     }
1517     
1518     public void mousePressed(MouseEvent e){
1519       
1520     }
1521     public void mouseReleased(MouseEvent e){
1522       
1523     }
1524     
1525     public void mouseEntered(MouseEvent e){
1526       
1527     }
1528     
1529     public void mouseExited(MouseEvent e){
1530       mouseMovementTimer.stop();
1531     }
1532   }//protected class TextMouseListener implements MouseInputListener
1533   
1534   
1535     
1536   protected class NewAnnotationSetAction extends AbstractAction{
1537     public NewAnnotationSetAction(){
1538       super("New");
1539       putValue(SHORT_DESCRIPTION, "Creates a new annotation set");
1540     }
1541     
1542     public void actionPerformed(ActionEvent evt){
1543       String name = newSetNameTextField.getText();
1544       newSetNameTextField.setText("");
1545       if(name != null && name.length() 0){
1546         AnnotationSet set = document.getAnnotations(name);
1547         //select the newly added set
1548         Iterator rowsIter = tableRows.iterator();
1549         int row = -1;
1550         for(int i = 0; i < tableRows.size() && row < 0; i++){
1551           if(tableRows.get(iinstanceof SetHandler &&
1552              ((SetHandler)tableRows.get(i)).set == setrow = i;
1553         }
1554         if(row >= 0mainTable.getSelectionModel().setSelectionInterval(row, row);
1555       }
1556     }
1557   }
1558 
1559   protected class NewAnnotationAction extends AbstractAction{
1560     public NewAnnotationAction(String selection){
1561       super("Create new annotation");
1562       putValue(SHORT_DESCRIPTION, "Creates a new annotation from the" +
1563         " selection: [" + Strings.crop(selection, 30"]");
1564     }
1565     public void actionPerformed(ActionEvent evt){
1566       if(annotationEditor == nullreturn;
1567       int start = textPane.getSelectionStart();
1568       int end = textPane.getSelectionEnd();
1569       if(start != end){
1570         textPane.setSelectionStart(start);
1571         textPane.setSelectionEnd(start);
1572         //create a new annotation
1573         //find the selected set
1574         int row = mainTable.getSelectedRow();
1575         //select the default annotation set if none selected
1576         if(row < 0row = 0;
1577         //find the set handler
1578         while(!(tableRows.get(rowinstanceof SetHandler)) row --;
1579         AnnotationSet set = ((SetHandler)tableRows.get(row)).set;
1580         try{
1581           Integer annId =  set.add(new Long(start)new Long(end)
1582                   lastAnnotationType, Factory.newFeatureMap());
1583           Annotation ann = set.get(annId);
1584           //select the annotation set in the tree view and expand it
1585           //to avoid the next annotation to be always in the default set
1586           if (tableRows.get(rowinstanceof SetHandler) {
1587             ((SetHandler)tableRows.get(row)).setExpanded(true);
1588             mainTable.getSelectionModel().setSelectionInterval(row, row);
1589           }
1590           //make sure new annotation is visible
1591           setTypeSelected(set.getName(), ann.getType()true);
1592           //edit the new annotation
1593           pendingEvents.offer(new PerformActionEvent(
1594                   new EditAnnotationAction(new AnnotationDataImpl(set, ann))));
1595           eventMinder.restart();
1596         }catch(InvalidOffsetException ioe){
1597           //this should never happen
1598           throw new GateRuntimeException(ioe);
1599         }
1600       }
1601     }
1602   }
1603   
1604   /**
1605    * The beginning is the same as {@link NameBearerHandle.SaveAsXmlAction}.
1606    */
1607   protected class SavePreserveFormatAction extends AbstractAction{
1608     public SavePreserveFormatAction(){
1609       super("Save Preserving Format");
1610       putValue(SHORT_DESCRIPTION,
1611         "Saves original markups and highlighted annotations");
1612     }
1613     
1614     public void actionPerformed(ActionEvent evt){
1615       Runnable runableAction = new Runnable(){
1616         public void run(){
1617           XJFileChooser fileChooser = MainFrame.getFileChooser();
1618           ExtensionFileFilter filter =
1619             new ExtensionFileFilter("XML files""xml""gml");
1620           fileChooser.addChoosableFileFilter(filter);
1621           fileChooser.setMultiSelectionEnabled(false);
1622           fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
1623           fileChooser.setDialogTitle("Select document to save ...");
1624 
1625           if (document.getSourceUrl() != null) {
1626             String fileName = "";
1627             try {
1628               fileName = document.getSourceUrl().toURI().getPath().trim();
1629             catch (URISyntaxException e) {
1630               fileName = document.getSourceUrl().getPath().trim();
1631             }
1632             if (fileName.equals(""|| fileName.equals("/")) {
1633               if (document.getNamedAnnotationSets().containsKey("Original markups")
1634                && !document.getAnnotations("Original markups").get("title").isEmpty()) {
1635                 // use the title annotation if any
1636                 try {
1637                   fileName = document.getContent().getContent(
1638                     document.getAnnotations("Original markups").get("title").firstNode().getOffset(),
1639                     document.getAnnotations("Original markups").get("title").lastNode().getOffset())
1640                     .toString();
1641                 catch(InvalidOffsetException e) {
1642                   e.printStackTrace();
1643                 }
1644               else {
1645                 fileName = document.getSourceUrl().toString();
1646               }
1647               // cleans the file name
1648               fileName = fileName.replaceAll("/""_");
1649             else {
1650               // replaces the extension with .xml
1651               fileName = fileName.replaceAll("\\.[a-zA-Z]{1,4}$"".xml");
1652             }
1653             // cleans the file name
1654             fileName = fileName.replaceAll("[^/a-zA-Z0-9._-]""_");
1655             fileName = fileName.replaceAll("__+""_");
1656             // adds a .xml extension if not present
1657             if (!fileName.endsWith(".xml")) { fileName += ".xml"}
1658             File file = new File(fileName);
1659             fileChooser.ensureFileIsVisible(file);
1660             fileChooser.setSelectedFile(file);
1661           }
1662 
1663           if(fileChooser.showSaveDialog(owner== JFileChooser.APPROVE_OPTION) {
1664             File selectedFile = fileChooser.getSelectedFile();
1665             if(selectedFile == nullreturn;
1666             StatusListener sListener = (StatusListener)Gate.getListeners().
1667               get("gate.event.StatusListener");
1668             if (sListener != null
1669               sListener.statusChanged("Please wait while dumping annotations"+
1670               "in the original format to " + selectedFile.toString() " ...");
1671             // This method construct a set with all annotations that need to be
1672             // dumped as Xml. If the set is null then only the original markups
1673             // are dumped.
1674             Set annotationsToDump = new HashSet();
1675             Iterator setIter = setHandlers.iterator();
1676             while(setIter.hasNext()){
1677               SetHandler sHandler = (SetHandler)setIter.next();
1678               Iterator typeIter = sHandler.typeHandlers.iterator();
1679               while(typeIter.hasNext()){
1680                 TypeHandler tHandler = (TypeHandler)typeIter.next();
1681                 if(tHandler.isSelected()){
1682                   annotationsToDump.addAll(sHandler.set.get(tHandler.name));
1683                 }
1684               }
1685             }
1686             
1687             try{
1688               // Prepare to write into the xmlFile using the original encoding
1689               String encoding = ((TextualDocument)document).getEncoding();
1690 
1691               OutputStreamWriter writer = new OutputStreamWriter(
1692                                             new FileOutputStream(selectedFile),
1693                                             encoding);
1694 
1695               //determine if the features need to be saved first
1696               Boolean featuresSaved =
1697                   Gate.getUserConfig().getBoolean(
1698                     GateConstants.SAVE_FEATURES_WHEN_PRESERVING_FORMAT);
1699               boolean saveFeatures = true;
1700               if (featuresSaved != null)
1701                 saveFeatures = featuresSaved.booleanValue();
1702               // Write with the toXml() method
1703               writer.write(
1704                 document.toXml(annotationsToDump, saveFeatures));
1705               writer.flush();
1706               writer.close();
1707               document.setSourceUrl(selectedFile.toURI().toURL());
1708             catch (Exception ex){
1709               ex.printStackTrace(Out.getPrintWriter());
1710             }// End try
1711             if (sListener != null)
1712               sListener.statusChanged("Finished dumping into the "+
1713               "file : " + selectedFile.toString());
1714           }// End if
1715         }// End run()
1716       };// End Runnable
1717       Thread thread = new Thread(runableAction, "");
1718       thread.setPriority(Thread.MIN_PRIORITY);
1719       thread.start();
1720     }
1721   }
1722   
1723   /**
1724    * A fake GATE Event used to wrap a {@link Runnable} value. This is used for
1725    * queueing actions to the document UI update timer.  
1726    */
1727   private class PerformActionEvent extends GateEvent{
1728     public PerformActionEvent(Runnable runnable){
1729       super(AnnotationSetsView.this, 0);
1730       this.runnable = runnable;
1731       this.action = null;
1732     }
1733 
1734     public PerformActionEvent(Action action){
1735       super(AnnotationSetsView.this, 0);
1736       this.runnable = null;
1737       this.action = action;
1738     }
1739     
1740     /**
1741      * Runs the action (or runnable) enclosed by this event. 
1742      */
1743     public void run(){
1744       if(runnable != null){
1745         runnable.run();
1746       }else if(action != null){
1747         action.actionPerformed(null);
1748       }
1749     }
1750     
1751     private Action action;
1752     
1753     private Runnable runnable;
1754   }
1755   
1756   protected class HandleDocumentEventsAction extends AbstractAction{
1757 
1758     public void actionPerformed(ActionEvent ev) {
1759       //see if we need to try again to rebuild from scratch
1760       if(uiDirty){
1761         //a previous call to rebuild has failed; try again
1762         rebuildDisplay();
1763         return;
1764       }
1765       //if too many individual events, then rebuild UI from scratch as it's 
1766       //faster.
1767       if(pendingEvents.size() > MAX_EVENTS){
1768         rebuildDisplay();
1769         return;
1770       }
1771       //process the individual events
1772       while(!pendingEvents.isEmpty()){
1773         GateEvent event = pendingEvents.remove();
1774         if(event instanceof DocumentEvent){
1775           DocumentEvent e = (DocumentEvent)event;
1776           if(event.getType() == DocumentEvent.ANNOTATION_SET_ADDED){
1777               String newSetName = e.getAnnotationSetName();
1778               SetHandler sHandler = new SetHandler(document.getAnnotations(newSetName));
1779               //find the right location for the new set
1780               //this is a named set and the first one is always the default one
1781               int i = 0;
1782               if(newSetName != null){
1783                 for(i = 1;
1784                     i < setHandlers.size() && 
1785                     ((SetHandler)setHandlers.get(i)).set.
1786                     getName().compareTo(newSetName<= 0;
1787                     i++);
1788               }
1789               setHandlers.add(i, sHandler);
1790               //update the tableRows list
1791               int j = 0;
1792               if(i > 0){
1793                 SetHandler previousHandler = (SetHandler)setHandlers.get(i -1);
1794                 //find the index for the previous handler - which is guaranteed to exist
1795                 for(; tableRows.get(j!= previousHandler; j++);
1796                 if(previousHandler.isExpanded()){
1797                   j += previousHandler.typeHandlers.size();
1798                 }
1799                 j++;
1800               }
1801               tableRows.add(j, sHandler);
1802               //update the table view
1803               tableModel.fireTableRowsInserted(j, j);
1804           }else if(event.getType() == DocumentEvent.ANNOTATION_SET_REMOVED){
1805             String setName = e.getAnnotationSetName();
1806             //find the handler and remove it from the list of handlers
1807             SetHandler sHandler = getSetHandler(setName);
1808             if(sHandler != null){
1809               sHandler.set.removeAnnotationSetListener(AnnotationSetsView.this);
1810               //remove highlights if any
1811               Iterator typeIter = sHandler.typeHandlers.iterator();
1812               while(typeIter.hasNext()){
1813                 TypeHandler tHandler = (TypeHandler)typeIter.next();
1814                 tHandler.setSelected(false);
1815               }
1816               setHandlers.remove(sHandler);
1817               //remove the set from the table
1818               int row = tableRows.indexOf(sHandler);
1819               tableRows.remove(row);
1820               int removed = 1;
1821               //remove the type rows as well
1822               if(sHandler.isExpanded())
1823                 for(int i = 0; i < sHandler.typeHandlers.size(); i++){ 
1824                   tableRows.remove(row);
1825                   removed++;
1826                 }
1827               tableModel.fireTableRowsDeleted(row, row + removed -1);
1828               sHandler.cleanup();
1829             }
1830           }else{
1831             //some other kind of event we don't care about
1832           }
1833         }else if(event instanceof AnnotationSetEvent){
1834           AnnotationSetEvent e = (AnnotationSetEvent)event;
1835           AnnotationSet set = (AnnotationSet)e.getSource();
1836           Annotation ann = e.getAnnotation();
1837           if(event.getType() == AnnotationSetEvent.ANNOTATION_ADDED){
1838             TypeHandler tHandler = getTypeHandler(set.getName(), ann.getType());
1839             if(tHandler == null){
1840               //new type for this set
1841               SetHandler sHandler = getSetHandler(set.getName());
1842               tHandler = sHandler.newType(ann.getType());
1843             }
1844             tHandler.annotationAdded(ann);    
1845           }else if(event.getType() == AnnotationSetEvent.ANNOTATION_REMOVED){
1846             TypeHandler tHandler = getTypeHandler(set.getName(), ann.getType());
1847             if(tHandler != nulltHandler.annotationRemoved(ann);
1848           }else{
1849             //some other kind of event we don't care about
1850           }
1851         }else if(event instanceof PerformActionEvent){
1852           ((PerformActionEvent)event).run();
1853         }else{
1854           //unknown type of event -> ignore
1855         }
1856       }
1857     }
1858     
1859     /**
1860      * This method is used to update the display by reading the associated
1861      * document when it is considered that doing so would be cheaper than 
1862      * acting on the events queued
1863      */
1864     protected void rebuildDisplay(){
1865       //if there is a process still running, we may get concurrent modification 
1866       //exceptions, in which case we should give up and try again later.
1867       //this method will always run from the UI thread, so no synchronisation 
1868       //is necessary
1869       uiDirty = false;
1870       try{
1871         //Ignore all pending events, as we're rebuilding from scratch.
1872         //Rotate once through the whole queue, filtering out events we want
1873         //to ignore.
1874         GateEvent event;
1875         pendingEvents.offer(END_OF_LIST);
1876         while((event = pendingEvents.poll()) != END_OF_LIST){
1877           if(event instanceof DocumentEvent || 
1878              event instanceof AnnotationSetEvent){
1879             //ignore event
1880           }else{
1881             //event of unknown type -> we re-queue it!
1882             pendingEvents.offer(event);
1883           }
1884         }
1885         //store selection state
1886         storeSelectedTypes();
1887         //release all resources
1888         for(SetHandler sHandler : setHandlers){
1889           sHandler.typeHandlers.clear();
1890           sHandler.typeHandlersByType.clear();
1891           sHandler.set.removeAnnotationSetListener(AnnotationSetsView.this);
1892         }
1893         setHandlers.clear();
1894         tableRows.clear();
1895         listView.removeAnnotations(listView.getAllAnnotations());
1896         //update the stack view
1897         stackView.updateStackView();
1898 //        textView.removeAllBlinkingHighlights();
1899         //rebuild the UI
1900         populateUI();
1901         
1902         //restore the selection
1903         restoreSelectedTypes();
1904         tableModel.fireTableDataChanged();
1905       }catch(Throwable t){
1906         //something happened, we need to give up
1907         uiDirty = true;
1908 //        t.printStackTrace();        
1909       }
1910     }
1911     
1912     boolean uiDirty = false;
1913     /**
1914      * Maximum number of events to treat individually. If we have more pending
1915      * events than this value, the UI will be rebuilt from scratch
1916      */
1917     private static final int MAX_EVENTS = 300;
1918   }
1919   
1920   /**
1921    * Used to select an annotation for editing.
1922    *
1923    */
1924   protected class MouseStoppedMovingAction extends AbstractAction{
1925     
1926     public void actionPerformed(ActionEvent evt){
1927       if(annotationEditor == nullreturn;
1928       //this action either creates a new annotation or starts editing an 
1929       //existing one. In either case we need first to make sure that the current
1930       //annotation is finished editing.
1931       if(!annotationEditor.editingFinished()) return;
1932       if(textLocation == -1return;
1933       JPopupMenu popup = new JPopupMenu();
1934 
1935       //check for selection hovering
1936       if(textPane.getSelectedText() != null
1937           && textPane.getSelectionStart() <= textLocation
1938           && textPane.getSelectionEnd() >= textLocation){
1939         //add 'New annotation' to the popup menu
1940         popup.add(new NewAnnotationAction(textPane.getSelectedText()));
1941         popup.addSeparator();
1942       }
1943 
1944       //check for annotations at location
1945       for(SetHandler setHandler : setHandlers) {
1946         for(Annotation ann : setHandler.set.get(
1947               Math.max(0l, textLocation-1),
1948               Math.min(document.getContent().size(), textLocation+1))) {
1949           if(setHandler.getTypeHandler(ann.getType()).isSelected()) {
1950             AnnotationDataImpl annotAtPoint =
1951               new AnnotationDataImpl(setHandler.set, ann);
1952             //add annotations to edit to the popup menu
1953             popup.add(new HighlightMenuItem(
1954               new EditAnnotationAction(annotAtPoint),
1955               annotAtPoint.getAnnotation().getStartNode().getOffset().intValue(),
1956               annotAtPoint.getAnnotation().getEndNode().getOffset().intValue(),
1957               popup));
1958           }
1959         }
1960       }
1961 
1962       if (popup.getComponentCount() == 0) {
1963         // nothing to do
1964       else if(popup.getComponentCount() == 1
1965         || (popup.getComponentCount() == 2
1966          && popup.getComponent(1instanceof JSeparator)) {
1967         //only one annotation, start the editing directly
1968         //or only one selection, add new annotation
1969         ((JMenuItem)popup.getComponent(0)).getAction().actionPerformed(evt);
1970       else //mouse hover a selection AND annotation(s)
1971         try{
1972           Rectangle rect =  textPane.modelToView(textLocation);
1973           //display the popup
1974           popup.show(textPane, rect.x + 10, rect.y);
1975         }catch(BadLocationException ble){
1976           throw new GateRuntimeException(ble);
1977         }
1978       }
1979     }
1980     
1981     public void setTextLocation(int textLocation){
1982       this.textLocation = textLocation;
1983     }
1984     int textLocation;
1985   }//protected class SelectAnnotationAction extends AbstractAction{
1986   
1987   
1988   /**
1989    * The popup menu items used to select annotations
1990    * Apart from the normal {@link javax.swing.JMenuItem} behaviour, this menu
1991    * item also highlights the annotation which it would select if pressed.
1992    */
1993   protected class HighlightMenuItem extends JMenuItem {
1994     public HighlightMenuItem(Action action, int startOffset, int endOffset, 
1995             JPopupMenu popup) {
1996       super(action);
1997       this.start = startOffset;
1998       this.end = endOffset;
1999       this.addMouseListener(new MouseAdapter() {
2000         public void mouseEntered(MouseEvent e) {
2001           showHighlight();
2002         }
2003 
2004         public void mouseExited(MouseEvent e) {
2005           removeHighlight();
2006         }
2007       });
2008       popup.addPopupMenuListener(new PopupMenuListener(){
2009         public void popupMenuWillBecomeVisible(PopupMenuEvent e){
2010           
2011         }
2012         public void popupMenuCanceled(PopupMenuEvent e){
2013           removeHighlight();
2014         }
2015         public void popupMenuWillBecomeInvisible(PopupMenuEvent e){
2016           removeHighlight();
2017         }
2018         
2019         
2020       });
2021     }
2022     
2023     protected void showHighlight(){
2024       try {
2025         highlight = textPane.getHighlighter().addHighlight(start, end,
2026                                         DefaultHighlighter.DefaultPainter);
2027       }catch(BadLocationException ble){
2028         throw new GateRuntimeException(ble.toString());
2029       }
2030 
2031     }
2032     
2033     protected void removeHighlight(){
2034       if(highlight != null){
2035         textPane.getHighlighter().removeHighlight(highlight);
2036         highlight = null;
2037       }
2038       
2039     }
2040 
2041     int start;
2042     int end;
2043     Action action;
2044     Object highlight;
2045   }
2046   
2047   
2048   
2049   protected class EditAnnotationAction extends AbstractAction{
2050     public EditAnnotationAction(AnnotationData aData){
2051       super(aData.getAnnotation().getType() " [" 
2052               (aData.getAnnotationSet().getName() == null "  " 
2053                 aData.getAnnotationSet().getName()) +
2054               "]");
2055       putValue(SHORT_DESCRIPTION, aData.getAnnotation().getFeatures().toString());
2056       this.aData = aData;
2057     }
2058     
2059     public void actionPerformed(ActionEvent evt){
2060       if(annotationEditor == nullreturn;
2061       //if the editor is done with the current annotation, we can move to the 
2062       //next one
2063       if(annotationEditor.editingFinished()){
2064         //queue an event to set the annotation as selected
2065         selectAnnotation(aData);
2066         //queue an event to show the annotation editor
2067         Runnable action = new Runnable() {
2068           public void run() {
2069             annotationEditor.editAnnotation(aData.getAnnotation()
2070                     aData.getAnnotationSet());
2071           }
2072         };
2073         pendingEvents.offer(new PerformActionEvent(action));
2074         eventMinder.restart();
2075       }
2076     }
2077     
2078     private AnnotationData aData;
2079   }
2080   
2081   protected class SetSelectedAnnotationsAction extends AbstractAction{
2082     public SetSelectedAnnotationsAction(boolean selected){
2083       String title = (selected"Select all" "Unselect all";
2084       putValue(NAME, title);
2085       putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("SPACE"));
2086       this.selected = selected;
2087     }
2088     public void actionPerformed(ActionEvent evt){
2089       List<Object> handlersToSelect = new ArrayList<Object>();
2090       int[] selectedRows = mainTable.getSelectedRows();
2091       Arrays.sort(selectedRows);
2092       for (int row : selectedRows) {
2093         Object handler = tableRows.get(row);
2094         if(handler instanceof SetHandler){
2095           // store the set handler
2096           handlersToSelect.add(0, handler);
2097         else if(handler instanceof TypeHandler
2098         && !handlersToSelect.contains(((TypeHandler)handler).setHandler)){
2099           // store the type handler
2100           // only if not included in a previous selected set handler
2101           handlersToSelect.add(handlersToSelect.size(), handler);
2102         }
2103       }
2104       for (Object handler : handlersToSelect) {
2105         if(handler instanceof TypeHandler){
2106           TypeHandler tHandler = (TypeHandler)handler;
2107           tHandler.setSelected(selected);
2108         }else if(handler instanceof SetHandler){
2109           SetHandler sHandler = (SetHandler)handler;
2110           for (String setName : sHandler.set.getAllTypes()) {
2111             TypeHandler tHandler = sHandler.getTypeHandler(setName);
2112             tHandler.setSelected(selected);
2113           }
2114         }
2115       }
2116     }
2117     boolean selected;
2118   }
2119 
2120   protected class DeleteSelectedAnnotationsAction extends AbstractAction{
2121     public DeleteSelectedAnnotationsAction(String name){
2122       putValue(NAME, name);
2123       putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("DELETE"));
2124     }
2125     public void actionPerformed(ActionEvent event){
2126       // builds the list of type and set handlers to delete
2127       Vector<String> resourcesToDelete = new Vector<String>()
2128       List<Object> handlersToDelete = new ArrayList<Object>();
2129       int[] selectedRows = mainTable.getSelectedRows();
2130       Arrays.sort(selectedRows);
2131       for (int row : selectedRows) {
2132         Object handler = tableRows.get(row);
2133         if(handler instanceof SetHandler){
2134           // store the set handler
2135           handlersToDelete.add(0, handler);
2136           String setName = ((SetHandlerhandler).set.getName();
2137           setName = (setName == null)"Default set" : setName;
2138           resourcesToDelete.add("set: " + setName);
2139         else if(handler instanceof TypeHandler
2140         && !handlersToDelete.contains(((TypeHandler)handler).setHandler)){
2141           // store the type handler
2142           // only if not included in a previous selected set handler
2143           handlersToDelete.add(handlersToDelete.size(), handler);
2144           String setName = ((TypeHandlerhandler).setHandler.set.getName();
2145           setName = (setName == null)"Default set" : setName;
2146           resourcesToDelete.add("type: " ((TypeHandlerhandler).name
2147             " in set: " + setName);
2148         }
2149       }
2150       if ((event.getModifiers() & ActionEvent.SHIFT_MASK)
2151                                != ActionEvent.SHIFT_MASK
2152        && (event.getModifiers() & InputEvent.BUTTON1_MASK)
2153                                != InputEvent.BUTTON1_MASK) {
2154         // shows a confirm dialog to delete types and sets
2155         JList list = new JList(resourcesToDelete);
2156         list.setVisibleRowCount(Math.min(resourcesToDelete.size()+110));
2157         int choice = JOptionPane.showOptionDialog(MainFrame.getInstance()new
2158           Object[]{"Are you sure you want to delete the following annotations?",
2159           '\n'new JScrollPane(list),
2160           "<html><i>You can use Shift+Delete to bypass this dialog.</i>\n\n"},
2161           "Delete annotations",
2162           JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null,
2163           new String[]{"Delete annotations""Cancel"}"Cancel");
2164         if (choice == JOptionPane.CLOSED_OPTION || choice == 1)  { return}
2165       }
2166       // deletes types and sets
2167       for (Object handler : handlersToDelete) {
2168         if(handler instanceof SetHandler){
2169           SetHandler sHandler = (SetHandler)handler;
2170           if(sHandler.set == document.getAnnotations()){
2171             //the default annotation set - clear
2172             for (Annotation annotation: new HashSet<Annotation>(sHandler.set)) {
2173               sHandler.set.remove(annotation);
2174             }
2175           }else{
2176             document.removeAnnotationSet(sHandler.set.getName());
2177           }
2178         else if(handler instanceof TypeHandler){
2179           TypeHandler tHandler = (TypeHandler)handler;
2180           AnnotationSet set = tHandler.setHandler.set;
2181           AnnotationSet toDeleteAS = set.get(tHandler.name);
2182           if(toDeleteAS != null && toDeleteAS.size() 0){
2183             List<Annotation> toDelete = new ArrayList<Annotation>(toDeleteAS);
2184             set.removeAll(toDelete);
2185           }
2186         }
2187       }
2188     }
2189   }  
2190   
2191   List<SetHandler> setHandlers;
2192   /** Contains the data of the main table. */
2193   List tableRows; 
2194   XJTable mainTable;
2195   SetsTableModel tableModel;
2196   JScrollPane scroller;
2197   JPanel mainPanel;
2198   JTextField newSetNameTextField;
2199   
2200   TextualDocumentView textView;
2201   AnnotationListView listView;
2202   AnnotationStackView stackView;
2203   JTextArea textPane;
2204   gate.gui.annedit.OwnedAnnotationEditor annotationEditor;
2205   NewAnnotationSetAction newSetAction;
2206   
2207   /**
2208    * The listener for mouse and mouse motion events in the text view.
2209    */
2210   protected TextMouseListener textMouseListener;
2211   
2212   /**
2213    * Listener for property changes on the text pane.
2214    */
2215   protected PropertyChangeListener textChangeListener;
2216   
2217   /**
2218    * Stores the list of visible annotation types when the view is inactivated 
2219    * so that the selection can be restored when the view is made active again.
2220    * The values are String[2] pairs of form <set name, type>.
2221    */
2222   protected BlockingQueue<TypeSpec> visibleAnnotationTypes;
2223   
2224   protected Timer mouseMovementTimer;
2225   /**
2226    * Timer used to handle events coming from the document
2227    */
2228   protected Timer eventMinder;
2229   
2230   protected BlockingQueue<GateEvent> pendingEvents;
2231   
2232   private static final int MOUSE_MOVEMENT_TIMER_DELAY = 500;
2233   protected MouseStoppedMovingAction mouseStoppedMovingAction;
2234   
2235   protected String lastAnnotationType = "_New_";
2236   
2237   protected List actions;
2238   
2239   protected final static ColorGenerator colourGenerator = new ColorGenerator();
2240   private static final int NAME_COL = 1;
2241   private static final int SELECTED_COL = 0;
2242   
2243   /**
2244    * A special GateEvent used as a flag.
2245    */
2246   private static final GateEvent END_OF_LIST = new GateEvent(
2247           AnnotationSetsView.class, 
2248           Integer.MAX_VALUE);
2249   
2250   private static final int EVENTS_HANDLE_DELAY = 300;
2251   
2252 }