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