AnnotationListView.java
001 /*
002  *  Copyright (c) 2009-2010, Ontotext AD.
003  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
004  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
005  *
006  *  This file is part of GATE (see http://gate.ac.uk/), and is free
007  *  software, licenced under the GNU Library General Public License,
008  *  Version 2, June 1991 (in the distribution as file licence.html,
009  *  and also available at http://gate.ac.uk/gate/licence.html).
010  *
011  *  AnnotatioListView.java
012  *
013  *  Valentin Tablan, May 25, 2004
014  *
015  *  $Id: AnnotationListView.java 17606 2014-03-09 12:12:49Z markagreenwood $
016  */
017 
018 package gate.gui.docview;
019 
020 import gate.*;
021 import gate.creole.*;
022 import gate.event.AnnotationEvent;
023 import gate.event.AnnotationListener;
024 import gate.gui.MainFrame;
025 import gate.gui.annedit.*;
026 import gate.swing.XJTable;
027 import gate.util.*;
028 import java.awt.*;
029 import java.awt.event.*;
030 import java.util.*;
031 import java.util.List;
032 import java.util.Timer;
033 import javax.swing.*;
034 import javax.swing.event.*;
035 import javax.swing.table.AbstractTableModel;
036 import javax.swing.text.JTextComponent;
037 
038 /**
039  * A tabular view for a list of annotations.
040  * Used as part of the document viewer to display all the annotation currently
041  * highlighted.
042  */
043 @SuppressWarnings("serial")
044 public class AnnotationListView extends AbstractDocumentView
045   implements AnnotationListener, AnnotationList, AnnotationEditorOwner{
046 
047   public AnnotationListView(){
048     annDataList = new ArrayList<AnnotationData>();
049     editorsCache = new HashMap<String, AnnotationVisualResource>();
050   }
051 
052 
053    /**
054     *  (non-Javadoc)
055     @see gate.gui.docview.AnnotationList#getAnnotationAtRow(int)
056     */
057    @Override
058   public AnnotationData getAnnotationAtRow(int row) {
059      return annDataList == null null : annDataList.get(
060              table.rowViewToModel(row));
061    }
062 
063 
064    /* (non-Javadoc)
065     * @see gate.gui.docview.AnnotationList#getSelectionModel()
066     */
067    @Override
068   public ListSelectionModel getSelectionModel() {
069      return table == null null : table.getSelectionModel();
070    }
071 
072 
073    @Override
074    public void cleanup() {
075      super.cleanup();
076      for(AnnotationData aData : annDataList){
077        aData.getAnnotation().removeAnnotationListener(this);
078      }
079      annDataList.clear();
080      textView = null;
081    }
082 
083    /* (non-Javadoc)
084     * @see gate.gui.docview.AbstractDocumentView#initGUI()
085     */
086    @Override
087   protected void initGUI() {
088      tableModel = new AnnotationTableModel();
089      table = new XJTable(tableModel);
090      table.setAutoResizeMode(XJTable.AUTO_RESIZE_LAST_COLUMN);
091      table.setSortable(true);
092      table.setSortedColumn(START_COL);
093      table.setIntercellSpacing(new Dimension(20));
094      table.setEnableHidingColumns(true);
095      scroller = new JScrollPane(table);
096 
097      mainPanel = new JPanel();
098      mainPanel.setLayout(new GridBagLayout());
099      GridBagConstraints constraints = new GridBagConstraints();
100 
101      constraints.gridx = 0;
102      constraints.gridwidth = 4;
103      constraints.gridy = 0;
104      constraints.weightx = 1;
105      constraints.weighty = 1;
106      constraints.fill= GridBagConstraints.BOTH;
107      mainPanel.add(scroller, constraints);
108 
109      constraints.gridx = GridBagConstraints.RELATIVE;
110      constraints.gridwidth = 1;
111      constraints.gridy = 1;
112      constraints.weightx = 0;
113      constraints.weighty = 0;
114      constraints.fill= GridBagConstraints.NONE;
115      constraints.anchor = GridBagConstraints.WEST;
116      statusLabel = new JLabel();
117      mainPanel.add(statusLabel, constraints);
118      constraints.fill= GridBagConstraints.HORIZONTAL;
119      constraints.anchor = GridBagConstraints.EAST;
120      mainPanel.add(Box.createHorizontalStrut(10), constraints);
121      mainPanel.add(new JLabel("Select: "), constraints);
122      filterTextField = new JTextField(20);
123      filterTextField.setToolTipText("Select the rows containing this text.");
124      mainPanel.add(filterTextField, constraints);
125 
126      //get a pointer to the text view used to display
127      //the selected annotations
128      Iterator<DocumentView> centralViewsIter = owner.getCentralViews().iterator();
129      while(textView == null && centralViewsIter.hasNext()){
130        DocumentView aView = centralViewsIter.next();
131        if(aView instanceof TextualDocumentView)
132          textView = (TextualDocumentView)aView;
133      }
134 
135      initListeners();
136    }
137 
138    @Override
139   public Component getGUI(){
140      return mainPanel;
141    }
142 
143    protected void initListeners(){
144 
145         tableModel.addTableModelListener(new TableModelListener(){
146           @Override
147           public void tableChanged(TableModelEvent e){
148             statusLabel.setText(
149                     Integer.toString(tableModel.getRowCount()) +
150                     " Annotations (" +
151                     Integer.toString(table.getSelectedRowCount()) +
152                     " selected)");
153           }
154         });
155 
156         table.getSelectionModel().
157           addListSelectionListener(new ListSelectionListener(){
158             @Override
159             public void valueChanged(ListSelectionEvent e){
160               if(!isActive())return;
161               if(e.getValueIsAdjusting()) return;
162               statusLabel.setText(
163                       Integer.toString(tableModel.getRowCount()) +
164                       " Annotations (" +
165                       Integer.toString(table.getSelectedRowCount()) +
166                       " selected)");
167               //if the new selection is already known about, no more work to do 
168               if(localSelectionUpdatingreturn;
169               //update the list of selected annotations globally
170               int[] viewRows = table.getSelectedRows();
171               List<AnnotationData> selAnns = new ArrayList<AnnotationData>();
172               for(int i = 0; i < viewRows.length; i++){
173                 int modelRow = table.rowViewToModel(viewRows[i]);
174                 if(modelRow >= 0){
175                   selAnns.add(annDataList.get(modelRow));
176                 }
177               }
178               owner.setSelectedAnnotations(selAnns);
179               
180               if(table.getSelectedRowCount() >= 1){
181                 int viewRow = table.getSelectionModel().getLeadSelectionIndex();
182                 if(table.getSelectionModel().isSelectedIndex(viewRow)){
183                   int modelRow = table.rowViewToModel(viewRow);
184                   AnnotationData aHandler = annDataList.get(modelRow);
185                   //scroll to show the last highlight
186                   if(aHandler != null && aHandler.getAnnotation() != null)
187                     textView.scrollAnnotationToVisible(aHandler.getAnnotation());
188                 }else{
189                   //last operation was a remove selection
190                   //do nothing
191                 }
192               }
193             }
194         });
195 
196         table.addMouseListener(new MouseAdapter() {
197           @Override
198           public void mouseClicked(MouseEvent me) {
199             processMouseEvent(me);
200           }
201           @Override
202           public void mouseReleased(MouseEvent me) {
203             processMouseEvent(me);
204           }
205           @Override
206           public void mousePressed(MouseEvent me) {
207             int row = table.rowAtPoint(me.getPoint());
208             if(me.isPopupTrigger()
209             && !table.isRowSelected(row)) {
210               // if right click outside the selection then reset selection
211               table.getSelectionModel().setSelectionInterval(row, row);
212             }
213             processMouseEvent(me);
214           }
215           protected void processMouseEvent(MouseEvent me){
216             int viewRow = table.rowAtPoint(me.getPoint());
217             final int modelRow = viewRow == -?
218                                  viewRow :
219                                  table.rowViewToModel(viewRow);
220 
221             // popup menu
222             if(me.isPopupTrigger()) {
223               JPopupMenu popup = new JPopupMenu();
224               popup.add(new DeleteAction());
225 
226               //add the custom edit actions
227               if(modelRow != -1){
228                 AnnotationData aHandler = annDataList.get(modelRow);
229                 popup.addSeparator();
230                 List<Action> specificEditorActions = getSpecificEditorActions(
231                   aHandler.getAnnotationSet(), aHandler.getAnnotation());
232                 for (Action action : specificEditorActions) {
233                   popup.add(action);
234                 }
235                 if (!(popup.getComponent(popup.getComponentCount()-1)
236                     instanceof JSeparator)) {
237                   popup.addSeparator();
238                 }
239                 for (Action action : getGenericEditorActions(
240                   aHandler.getAnnotationSet(), aHandler.getAnnotation())) {
241                   if (specificEditorActions.contains(action)) { continue}
242                   popup.add(action);
243                 }
244                 if ((popup.getComponent(popup.getComponentCount()-1)
245                     instanceof JSeparator)) {
246                   popup.remove(popup.getComponentCount()-1);
247                 }
248               }
249               popup.show(table, me.getX(), me.getY());
250             }
251           }
252         });
253 
254         table.addAncestorListener(new AncestorListener() {
255           @Override
256           public void ancestorAdded(AncestorEvent event) {
257             // force the table to be sorted when the view is shown
258             tableModel.fireTableDataChanged();
259           }
260           @Override
261           public void ancestorMoved(AncestorEvent event) {
262           }
263           @Override
264           public void ancestorRemoved(AncestorEvent event) {
265           }
266         });
267 
268     // select all the rows containing the text from filterTextField
269     filterTextField.getDocument().addDocumentListenernew DocumentListener() {
270       private Timer timer = new Timer("Annotation list selection timer"true);
271       private TimerTask timerTask;
272       @Override
273       public void changedUpdate(DocumentEvent e) { /* do nothing */ }
274       @Override
275       public void insertUpdate(DocumentEvent e) { update()}
276       @Override
277       public void removeUpdate(DocumentEvent e) { update()}
278       private void update() {
279         if (timerTask != null) { timerTask.cancel()}
280         Date timeToRun = new Date(System.currentTimeMillis() 300);
281         timerTask = new TimerTask() { @Override
282         public void run() {
283           selectRows();
284         }};
285         // add a delay
286         timer.schedule(timerTask, timeToRun);
287       }
288       private void selectRows() {
289         table.clearSelection();
290         if (filterTextField.getText().trim().length() 2
291          || table.getRowCount() == 0) {
292           return;
293         }
294         // block upward events
295         localSelectionUpdating = true;
296         for (int row = 0; row < table.getRowCount(); row++) {
297           for (int col = 0; col < table.getColumnCount(); col++) {
298             if (table.getValueAt(row, col!= null
299              && table.getValueAt(row, col).toString()
300                 .contains(filterTextField.getText().trim())) {
301               table.addRowSelectionInterval(row, row);
302               break;
303             }
304           }
305         }
306         localSelectionUpdating = false;
307         // update the highlights in the document
308         if (table.isCellSelected(0,0)) {
309           table.addRowSelectionInterval(00);
310         else {
311           table.removeRowSelectionInterval(00);
312         }
313       }
314     });
315 
316     // Delete key for deleting selected annotations
317     table.addKeyListener(new KeyAdapter() {
318      @Override
319     public void keyPressed(KeyEvent e) {
320        if (e.getKeyCode() == KeyEvent.VK_DELETE) {
321          new DeleteAction().actionPerformed(new ActionEvent(e.getSource(),
322            e.getID()"", e.getWhen(), e.getModifiers()));
323        }
324      }
325     });
326   }
327 
328   public List<Action> getSpecificEditorActions(AnnotationSet set,
329                                                Annotation annotation) {
330     List<Action> actions = new ArrayList<Action>();
331     //add the specific editors
332     List<String> specificEditorClasses =
333       Gate.getCreoleRegister().getAnnotationVRs(annotation.getType());
334     if (specificEditorClasses != null &&
335       specificEditorClasses.size() 0) {
336       for (String editorClass : specificEditorClasses) {
337         AnnotationVisualResource editor =
338           editorsCache.get(editorClass);
339         if (editor == null) {
340           //create the new type of editor
341           try {
342             editor = (AnnotationVisualResource)
343               Factory.createResource(editorClass);
344             editorsCache.put(editorClass, editor);
345           catch (ResourceInstantiationException rie) {
346             rie.printStackTrace(Err.getPrintWriter());
347           }
348         }
349         actions.add(new EditAnnotationAction(set, annotation, editor));
350       }
351     }
352     return actions;
353   }
354 
355   public List<Action> getGenericEditorActions(AnnotationSet set,
356                                               Annotation annotation) {
357     List<Action> actions = new ArrayList<Action>();
358     //add generic editors
359     List<String> genericEditorClasses = Gate.getCreoleRegister().
360       getAnnotationVRs();
361     if (genericEditorClasses != null &&
362       genericEditorClasses.size() 0) {
363       for (String editorClass : genericEditorClasses) {
364         AnnotationVisualResource editor = editorsCache.get(editorClass);
365         if (editor == null) {
366           //create the new type of editor
367           try {
368             ResourceData resData = Gate.getCreoleRegister().get(editorClass);
369             Class<?> resClass = resData.getResourceClass();
370             if (OwnedAnnotationEditor.class.isAssignableFrom(resClass)) {
371               OwnedAnnotationEditor newEditor =
372                 (OwnedAnnotationEditorresClass.newInstance();
373               newEditor.setOwner(AnnotationListView.this);
374               newEditor.init();
375               editor = newEditor;
376             else {
377               editor = (AnnotationVisualResource)
378                 Factory.createResource(editorClass);
379             }
380             editorsCache.put(editorClass, editor);
381           catch (Exception rie) {
382             rie.printStackTrace(Err.getPrintWriter());
383           }
384         }
385         actions.add(new EditAnnotationAction(set, annotation, editor));
386       }
387     }
388     return actions;
389   }
390 
391   protected class DeleteAction extends AbstractAction {
392     public DeleteAction() {
393       super("Delete Annotations");
394       putValue(SHORT_DESCRIPTION, "Delete selected annotations");
395       putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("DELETE"));
396     }
397     @Override
398     public void actionPerformed(ActionEvent event){
399       if ((event.getModifiers() & ActionEvent.SHIFT_MASK)
400                                != ActionEvent.SHIFT_MASK
401        && (event.getModifiers() & InputEvent.BUTTON1_MASK)
402                                != InputEvent.BUTTON1_MASK) {
403         // shows a confirm dialog before to delete annotations
404         int choice = JOptionPane.showOptionDialog(MainFrame.getInstance()new
405           Object[]{"Are you sure you want to delete the "
406           + table.getSelectedRowCount() " selected annotations?",
407           "<html><i>You can use Shift+Delete to bypass this dialog.</i>\n\n"},
408           "Delete annotations",
409           JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null,
410           new String[]{"Delete annotations""Cancel"}"Cancel");
411         if (choice == JOptionPane.CLOSED_OPTION || choice == 1)  { return}
412       }
413       List<AnnotationData> annotationsData = new ArrayList<AnnotationData>();
414       for (int selectedRow : table.getSelectedRows()) {
415         annotationsData.add(annDataList.get(table.rowViewToModel(selectedRow)));
416       }
417       for (AnnotationData annotationData : annotationsData) {
418         annotationData.getAnnotationSet().remove(annotationData.getAnnotation());
419       }
420     }
421   }
422 
423    /* (non-Javadoc)
424        * @see gate.gui.docview.AbstractDocumentView#registerHooks()
425        */
426       @Override
427       protected void registerHooks() {
428         //this is called on activation
429     //    showHighlights();
430       }
431 
432    /* (non-Javadoc)
433     * @see gate.gui.docview.AbstractDocumentView#unregisterHooks()
434     */
435    @Override
436   protected void unregisterHooks() {
437      //this is called on de-activation
438      //remove highlights
439  //    textView.removeAllBlinkingHighlights();
440    }
441 
442    /* (non-Javadoc)
443     * @see gate.gui.docview.DocumentView#getType()
444     */
445    @Override
446   public int getType() {
447      return HORIZONTAL;
448    }
449 
450  //  protected void showHighlights(){
451  //    int[] viewRows = table.getSelectedRows();
452  //    AnnotationData aHandler = null;
453  //    for(int i = 0; i < viewRows.length; i++){
454  //      int modelRow = table.rowViewToModel(viewRows[i]);
455  //      if(modelRow >= 0){
456  //        aHandler = annDataList.get(modelRow);
457  //        textView.addBlinkingHighlight(aHandler.getAnnotation());
458  //      }
459  //    }
460  //  }
461 
462 
463    /**
464     * Adds an annotation to be displayed in the list.
465     @param ann the annotation
466     @param set the set containing the annotation
467     @return a tag that can be used to refer to this annotation for future
468     * operations, e.g. when removing the annotation from display.
469     */
470    public AnnotationDataImpl addAnnotation(Annotation ann, AnnotationSet set){
471      AnnotationDataImpl aData = new AnnotationDataImpl(set, ann);
472      annDataList.add(aData);
473      int row = annDataList.size() -1;
474      try{
475        localSelectionUpdating = true;
476        if(tableModel != nulltableModel.fireTableRowsInserted(row, row);
477      }finally{
478        localSelectionUpdating = false;
479      }
480 
481      //listen for the new annotation's events
482      aData.getAnnotation().addAnnotationListener(AnnotationListView.this);
483      return aData;
484    }
485 
486    public void removeAnnotation(AnnotationData tag){
487      int row = annDataList.indexOf(tag);
488      if(row >= 0){
489        AnnotationData aHandler = annDataList.get(row);
490        //remove from selection, if the table is built
491        List<AnnotationData> selAnns = owner.getSelectedAnnotations();
492        if(selAnns.remove(tag)){
493          owner.setSelectedAnnotations(selAnns);
494        }
495        aHandler.getAnnotation().removeAnnotationListener(
496                AnnotationListView.this);
497        annDataList.remove(row);
498        //owner was already notified
499        try{
500          localSelectionUpdating = true;
501          if(tableModel != nulltableModel.fireTableRowsDeleted(row, row);
502        }finally{
503          localSelectionUpdating = false;
504      }
505    }
506    }
507 
508    public void removeAnnotations(Collection<AnnotationData> tags){
509      //to speed-up things, first remove all blinking highlights
510      if(table != null){
511        table.getSelectionModel().clearSelection();
512      }
513      //cache the selected annotations
514      final List<AnnotationData> selAnns = owner.getSelectedAnnotations();
515      boolean selectionChanged = false;
516      //now do the actual removal, in batch mode
517      for(AnnotationData aData : tags){
518        annDataList.remove(aData);
519        if(selAnns.remove(aData)){
520          selectionChanged = true;
521        }
522        aData.getAnnotation().removeAnnotationListener(AnnotationListView.this);
523      }
524      //update the table display
525      if(tableModel != nulltableModel.fireTableDataChanged();
526      //restore selection
527      if(selectionChanged){
528        //this needs to happen after the table has caught up with all the changes
529        //hence we need to queue it to the GUI thread
530        SwingUtilities.invokeLater(new Runnable(){
531        @Override
532       public void run(){
533          owner.setSelectedAnnotations(selAnns);
534        }});
535      }
536    }
537 
538    /**
539     * Adds a batch of annotations in one go.
540     * For each annotation, a tag object will be created. The return value is
541     * list that returns the tags in the same order as the collection used
542     * for the input annotations.
543     * This method does not assume it was called from the UI Thread.
544     @return a collection of tags corresponding to the annotations.
545     @param annotations a collection of annotations
546     @param set the annotation set to which all the annotations belong.
547     */
548    public List<AnnotationData> addAnnotations(List<Annotation> annotations,
549            AnnotationSet set){
550      List<AnnotationData> tags = new ArrayList<AnnotationData>();
551      for(Annotation ann : annotations){
552        AnnotationData aTag = new AnnotationDataImpl(set, ann);
553        tags.add(aTag);
554        annDataList.add(aTag);
555        ann.addAnnotationListener(AnnotationListView.this);
556      }
557      try{
558        //this will cause the selection to change (the actual selection contents
559        //stay the same, but the row numbers may change)
560        //we want to avoid circular notifications.
561        localSelectionUpdating  = true;
562        if(tableModel != nulltableModel.fireTableDataChanged();
563      }finally{
564        localSelectionUpdating = false;
565      }
566      return tags;
567    }
568 
569    /**
570     * Returns the tags for all the annotations currently displayed
571     @return a list of {@link AnnotationDataImpl}.
572     */
573    public List<AnnotationData> getAllAnnotations(){
574      return annDataList;
575    }
576 
577    @Override
578   public void annotationUpdated(AnnotationEvent e){
579      //update all occurrences of this annotation
580     // if annotations tab has not been set to visible state
581      // table will be null.
582      if(table == null)  return;
583      //save selection
584      int[] selection = table.getSelectedRows();
585      if(selection != null){
586        localSelectionUpdating = true;
587      }
588      Annotation ann = (Annotation)e.getSource();
589      if(tableModel != null){
590        for(int i = 0; i < annDataList.size(); i++){
591          AnnotationData aHandler = annDataList.get(i);
592          if(aHandler.getAnnotation() == ann)tableModel.fireTableRowsUpdated(i, i);
593        }
594      }
595      //restore selection
596      table.clearSelection();
597      if(selection != null){
598        localSelectionUpdating = true;
599        for(int i = 0; i < selection.length; i++){
600          table.addRowSelectionInterval(selection[i], selection[i]);
601        }
602        localSelectionUpdating = false;
603      }
604    }
605 
606 
607    /* (non-Javadoc)
608     * @see gate.gui.docview.AbstractDocumentView#setSelectedAnnotations(java.util.List)
609     */
610    @Override
611    public void setSelectedAnnotations(final List<AnnotationData> selectedAnnots) {
612      //if the list of selected annotations differs from the current selection,
613      //update the selection.
614      //otherwise do nothing (to break infinite looping)
615 
616      //first get the local list of selected annotations
617      int[] viewRows = table.getSelectedRows();
618      List<AnnotationData> localSelAnns = new ArrayList<AnnotationData>();
619      for (int viewRow : viewRows) {
620        int modelRow = table.rowViewToModel(viewRow);
621        if (modelRow >= 0) {
622          localSelAnns.add(annDataList.get(modelRow));
623        }
624      }
625      //now compare with the new value
626      if(localSelAnns.size() == selectedAnnots.size()){
627        //same size, we need to actually compare contents
628        localSelAnns.removeAll(selectedAnnots);
629        if(localSelAnns.isEmpty()){
630          //lists are the same -> exit!
631          return;
632        }
633      }
634      //if we got this far, the selection lists were different
635      //we need to change the selection from the UI thread.
636      SwingUtilities.invokeLater(new Runnable(){
637        @Override
638       public void run(){
639          try{
640            //block upward events
641            localSelectionUpdating = true;
642            //update the local selection
643            table.getSelectionModel().clearSelection();
644            int rowToScrollTo = -1;
645            for(AnnotationData aData : selectedAnnots){
646              int modelRow = annDataList.indexOf(aData);
647              if(modelRow != -1){
648                int viewRow = table.rowModelToView(modelRow);
649                table.getSelectionModel().addSelectionInterval(viewRow, viewRow);
650                rowToScrollTo = viewRow;
651              }
652            }
653            if(rowToScrollTo >= 0){
654              table.scrollRectToVisible(table.getCellRect(rowToScrollTo, 0true));
655            }
656          }finally{
657            //re-enable upward events
658            localSelectionUpdating = false;
659          }
660        }
661      });
662    }
663 
664 
665    /**
666     * Selects the annotation for the given tag.
667     @param tag the tag of the annotation to be selected.
668     */
669    public void selectAnnotationForTag(Object tag){
670      int modelPosition = annDataList.indexOf(tag);
671      table.getSelectionModel().clearSelection();
672      if(modelPosition != -1){
673        int tablePosition = table.rowModelToView(modelPosition);
674        table.getSelectionModel().setSelectionInterval(tablePosition,
675                tablePosition);
676        table.scrollRectToVisible(table.getCellRect(tablePosition, 0false));
677      }
678    }
679 
680 
681 
682    /* (non-Javadoc)
683     * @see gate.gui.annedit.AnnotationEditorOwner#annotationChanged(gate.Annotation, gate.AnnotationSet, java.lang.String)
684     */
685    @Override
686   public void annotationChanged(Annotation ann, AnnotationSet set,
687            String oldType) {
688      //do nothing
689    }
690 
691    /* (non-Javadoc)
692     * @see gate.gui.annedit.AnnotationEditorOwner#getNextAnnotation()
693     */
694    @Override
695   public Annotation getNextAnnotation() {
696      return null;
697    }
698 
699 
700    /* (non-Javadoc)
701     * @see gate.gui.annedit.AnnotationEditorOwner#getPreviousAnnotation()
702     */
703    @Override
704   public Annotation getPreviousAnnotation() {
705      return null;
706    }
707 
708 
709    /* (non-Javadoc)
710     * @see gate.gui.annedit.AnnotationEditorOwner#getTextComponent()
711     */
712    @Override
713   public JTextComponent getTextComponent() {
714      //get a pointer to the text view used to display
715      //the selected annotations
716      Iterator<DocumentView> centralViewsIter = owner.getCentralViews().iterator();
717      while(textView == null && centralViewsIter.hasNext()){
718        DocumentView aView = centralViewsIter.next();
719        if(aView instanceof TextualDocumentView)
720          textView = (TextualDocumentView)aView;
721      }
722      return (JTextArea)((JScrollPane)textView.getGUI()).getViewport().getView();
723    }
724 
725    /* (non-Javadoc)
726     * @see gate.gui.annedit.AnnotationEditorOwner#selectAnnotation(gate.gui.annedit.AnnotationData)
727     */
728    @Override
729   public void selectAnnotation(AnnotationData data) {
730    }
731 
732    /* (non-Javadoc)
733     * @see gate.gui.docview.AnnotationList#getRowForAnnotation(gate.gui.annedit.AnnotationData)
734     */
735    @Override
736   public int getRowForAnnotation(AnnotationData data) {
737      return annDataList.indexOf(data);
738    }
739 
740   class AnnotationTableModel extends AbstractTableModel{
741        @Override
742       public int getRowCount(){
743          return annDataList.size();
744        }
745 
746        @Override
747       public int getColumnCount(){
748          return 6;
749        }
750 
751        @Override
752       public String getColumnName(int column){
753          switch(column){
754            case TYPE_COL: return "Type";
755            case SET_COL: return "Set";
756            case START_COL: return "Start";
757            case END_COL: return "End";
758            case ID_COL: return "Id";
759            case FEATURES_COL: return "Features";
760            defaultreturn "?";
761          }
762        }
763 
764        @Override
765       public Class<?> getColumnClass(int column){
766          switch(column){
767            case TYPE_COL: return String.class;
768            case SET_COL: return String.class;
769            case START_COL: return Long.class;
770            case END_COL: return Long.class;
771            case ID_COL: return Integer.class;
772            case FEATURES_COL: return String.class;
773            defaultreturn String.class;
774          }
775        }
776 
777        @Override
778       public boolean isCellEditable(int rowIndex, int columnIndex){
779          return false;
780        }
781 
782        @SuppressWarnings("unchecked")
783       @Override
784       public Object getValueAt(int row, int column){
785          if(row >= annDataList.size()) return null;
786          AnnotationData aData = annDataList.get(row);
787          switch(column){
788            case TYPE_COL: return aData.getAnnotation().getType();
789            case SET_COL: return aData.getAnnotationSet().getName();
790            case START_COL: return aData.getAnnotation().getStartNode().getOffset();
791            case END_COL: return aData.getAnnotation().getEndNode().getOffset();
792            case ID_COL: return aData.getAnnotation().getId();
793            case FEATURES_COL:
794              //sort the features by name
795              FeatureMap features = aData.getAnnotation().getFeatures();
796              @SuppressWarnings("rawtypes")
797             List keyList = new ArrayList(features.keySet());
798              Collections.sort(keyList);
799              StringBuffer strBuf = new StringBuffer("{");
800              Iterator<Object> keyIter = keyList.iterator();
801              boolean first = true;
802              while(keyIter.hasNext()){
803                Object key = keyIter.next();
804                Object value = features.get(key);
805                if(first){
806                  first = false;
807                }else{
808                  strBuf.append(", ");
809                }
810                strBuf.append(key.toString());
811                strBuf.append("=");
812                strBuf.append(value == null "[null]" : value.toString());
813              }
814              strBuf.append("}");
815              return strBuf.toString();
816            defaultreturn "?";
817          }
818        }
819 
820      }
821 
822 
823   protected class EditAnnotationAction extends AbstractAction{
824        public EditAnnotationAction(AnnotationSet set, Annotation ann,
825                AnnotationVisualResource editor){
826          this.set = set;
827          this.ann = ann;
828          this.editor = editor;
829          ResourceData rData =
830            Gate.getCreoleRegister().get(editor.getClass().getName());
831          if(rData != null){
832            title = rData.getName();
833            putValue(NAME, "Edit with " + title);
834            putValue(SHORT_DESCRIPTION, rData.getComment());
835          }
836        }
837 
838        @Override
839       public void actionPerformed(ActionEvent evt){
840    //      editor.setTarget(set);
841    //      editor.setAnnotation(ann);
842          if(editor instanceof OwnedAnnotationEditor){
843            //we need to unpin the editor so that it actually calculates the
844            //position
845            ((OwnedAnnotationEditor)editor).setPinnedMode(false);
846            ((OwnedAnnotationEditor)editor).placeDialog(
847                    ann.getStartNode().getOffset().intValue(),
848                    ann.getEndNode().getOffset().intValue());
849            //now we need to pin it so that it does not disappear automatically
850            ((OwnedAnnotationEditor)editor).setPinnedMode(true);
851            editor.editAnnotation(ann, set);
852          }else{
853            editor.editAnnotation(ann, set);
854            JScrollPane scroller = new JScrollPane((Component)editor);
855            JOptionPane optionPane = new JOptionPane(scroller,
856                    JOptionPane.QUESTION_MESSAGE,
857                    JOptionPane.OK_CANCEL_OPTION,
858                    null, new String[]{"OK""Cancel"});
859            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
860            scroller.setMaximumSize(new Dimension((int)(screenSize.width * .75),
861                    (int)(screenSize.height * .75)));
862            JDialog dialog = optionPane.createDialog(AnnotationListView.this.getGUI(),
863                    title);
864            dialog.setModal(true);
865            dialog.setResizable(true);
866            dialog.setVisible(true);
867            try{
868              if(optionPane.getValue().equals("OK")) editor.okAction();
869              else editor.cancelAction();
870            }catch(GateException ge){
871              throw new GateRuntimeException(ge);
872            }
873 
874          }
875 
876        }
877 
878        String title;
879        Annotation ann;
880        AnnotationSet set;
881        AnnotationVisualResource editor;
882      }
883 
884      protected XJTable table;
885      protected AnnotationTableModel tableModel;
886      protected JScrollPane scroller;
887 
888      /**
889       * Stores the {@link AnnotationData} objects representing the annotations
890       * displayed by this view.
891       */
892      protected List<AnnotationData> annDataList;
893 
894      /**
895       * Flag used to mark the fact that the table selection is currently being
896       * updated, to synchronise it with the global selection.
897       * This is used to block update events being sent to the owner, while the
898       * current selection is adjusted.
899       */
900      protected volatile boolean localSelectionUpdating = false;
901 
902      protected JPanel mainPanel;
903      protected JLabel statusLabel;
904      protected JTextField filterTextField;
905      protected TextualDocumentView textView;
906      /**
907       * A map that stores instantiated annotations editors in order to avoid the
908       * delay of building them at each request;
909       */
910      protected Map<String, AnnotationVisualResource> editorsCache;
911 
912      private static final int TYPE_COL = 0;
913      private static final int SET_COL = 1;
914      private static final int START_COL = 2;
915      private static final int END_COL = 3;
916      private static final int ID_COL = 4;
917      private static final int FEATURES_COL = 5;
918 
919    }